该篇是关于算法的剑指offer刷题系列
剑指offer72题 数组 JZ3 数组中重复的数字 描述 在一个长度为n的数组里的所有数字都在0到n-1的范围内。 数组中某些数字是重复的,但不知道有几个数字是重复的。也不知道每个数字重复几次。请找出数组中任意一个重复的数字。 例如,如果输入长度为7的数组[2,3,1,0,2,5,3],那么对应的输出是2或者3。存在不合法的输入的话输出-1
数据范围:0≤𝑛≤10000 0≤n ≤10000
进阶:时间复杂度𝑂(𝑛) O (n ) ,空间复杂度𝑂(𝑛) O (n )
示例 1 2 3 输入:[2,3,1,0,2,5,3] 返回值:2 说明:2或3都是对的
解题 方案1: 暴力法(for + for)
方案2: 哈希表
1 2 3 4 5 6 7 8 9 10 11 class Solution : def duplicate (self , numbers: List [int ] ) -> int : record = {} // // 用来记录已遍历的数 for num in numbers: if num not in record: record[num] = 1 else : return num return -1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 public class Solution { public int duplicate (int [] numbers) { if (numbers == null || numbers.length == 0 ){ return -1 ; } HashSet<Integer> record = new HashSet <>(); for (int num : numbers){ if (record.contains(num)){ return num; } else { record.add(num); } } return -1 ; } }
方案3: 可以使用两数之和的index<--->value
思想
1 2 3 4 5 6 7 8 9 10 11 12 13 class Solution : def duplicate (self , numbers: List [int ] ) -> int : if ( len (numbers) < 2 ): return -1 record = [0 ] * len (numbers) for num in numbers: record[num] += 1 if record[num] == 2 : return num return -1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 public class Solution { public int duplicate (int [] numbers) { int [] record = new int [numbers.length]; for (int i = 0 ; i < numbers.length; i++){ record[numbers[i]]++; if (record[numbers[i]] == 2 ){ return numbers[i]; } } return -1 ; } }
方案3优化: 将两个List用一个原始List实现
1 2 3 4 5 6 7 8 9 10 11 class Solution : def duplicate (self , numbers: List [int ] ) -> int : for idx in range (len (numbers)): while idx != numbers[idx]: if numbers[idx] == numbers[numbers[idx]]: return numbers[idx] numbers[numbers[idx]], numbers[idx] = numbers[idx], numbers[numbers[idx]] return -1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public class Solution { public int duplicate (int [] numbers) { for (int i = 0 ; i < numbers.length; i++){ while (i != numbers[i]){ if (numbers[i] == numbers[numbers[i]]){ return numbers[i]; } int temp = numbers[numbers[i]]; numbers[numbers[i]] = numbers[i]; numbers[i] = temp; } } return -1 ; } }
JZ5 替换空格 描述 请实现一个函数,将一个字符串s中的每个空格替换成“%20”。
例如,当字符串为We Are Happy.则经过替换之后的字符串为We%20Are%20Happy。
数据范围:0≤𝑙𝑒𝑛(𝑠)≤1000 0≤le n (s )≤1000 。保证字符串中的字符为大写英文字母、小写英文字母和空格中的一种。
示例 1 2 输入:"We Are Happy" 返回值:"We%20Are%20Happy"
解题 方案1: 直接遍历,遇到则转换,将遍历结果添加到新的String中。
补充知识点:java中String追加char的三种方法——Add Cahr To String For Java
1 2 3 4 5 6 7 8 9 10 class Solution : def replaceSpace (self , s: str ) -> str : new_s = "" for char in s: if char == " " : char = "%20" new_s += char return new_s
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public class Solution { public String replaceSpace (String s) { String newS = "" ; char [] ch = s.toCharArray(); for (char c: ch){ if (c == ' ' ){ newS += '%' ; newS += '2' ; newS += '0' ; } else newS = newS + c; } return newS; } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public class Solution { public String replaceSpace (String s) { StringBuffer sb = new StringBuffer (); char [] ch = s.toCharArray(); for (char c: ch){ if (c == ' ' ){ sb.append('%' ); sb.append('2' ); sb.append('0' ); }else sb.append(c); } return sb.toString(); } }
方案2: 字符串扩容,扩容 空格字符*2 个单元,倒序遍历字符串,追加到扩容字符,按照条件进行添加处理
JZ17 打印从1到最大的n位数 描述 输入数字 n,按顺序打印出从 1 到最大的 n 位十进制数。比如输入 3,则打印出 1、2、3 一直到最大的 3 位数 999。
用返回一个整数列表来代替打印
n 为正整数,0 < n <= 5
示例 1 2 输入:1 返回值:[1,2,3,4,5,6,7,8,9]
解题 方案1: 直接暴力遍历(注意边界条件)
1 2 3 4 5 class Solution : def printNumbers (self , n: int ) -> List [int ]: return [number for number in range (1 , 10 **n)]
1 2 3 4 5 6 7 8 9 10 public class Solution { public int [] printNumbers (int n) { int [] res = new int [(int )Math.pow(10 ,n) - 1 ]; for (int i = 1 ; i < Math.pow(10 , n); i++){ res[i-1 ] = i; } return res; } }
方案2:
JZ21 调整数组顺序使奇数位于偶数前面(一) 描述 输入一个长度为 n 整数数组,实现一个函数来调整该数组中数字的顺序,使得所有的奇数位于数组的前面部分,所有的偶数位于数组的后面部分,并保证奇数和奇数,偶数和偶数之间的相对位置不变。
数据范围:0≤𝑛≤50000≤n ≤5000,数组中每个数的值 0≤𝑣𝑎𝑙≤100000≤va l ≤10000
要求:时间复杂度 𝑂(𝑛)O (n ),空间复杂度 𝑂(𝑛)O (n )
进阶:时间复杂度 𝑂(𝑛2)O (n 2),空间复杂度 𝑂(1)O (1)
示例 1 2 输入:[1,2,3,4] 返回值:[1,3,2,4]
解题 方案1: 奇偶拼接
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 class Solution : def reOrderArray (self , array: List [int ] ) -> List [int ]: new_arr = [0 ] * len (array) count = 0 for num in array: if num % 2 == 1 : count += 1 tow = 0 for idx in range (len (array)): if not array[idx] % 2 : new_arr[count] = array[idx] count += 1 else : new_arr[tow] = array[idx] tow += 1 return new_arr
1 2 3 4 5 6 7 8 9 10 11 12 class Solution : def reOrderArray (self , array: List [int ] ) -> List [int ]: new_arr = [] for num in array: if num % 2 : new_arr += [num] for num in array: if not num % 2 : new_arr += [num] return new_arr
1 2 3 4 5 6 7 8 9 10 public class Solution { public int [] printNumbers (int n) { int [] res = new int [(int )Math.pow(10 ,n) - 1 ]; for (int i = 1 ; i < Math.pow(10 , n); i++){ res[i-1 ] = i; } return res; } }
方案2: 全排列(考虑大数情况)
Z81 调整数组顺序使奇数位于偶数前面(二) 描述 输入一个长度为 n 整数数组,数组里面可能含有相同的元素,实现一个函数来调整该数组中数字的顺序,使得所有的奇数位于数组的前面部分,所有的偶数位于数组的后面部分,对奇数和奇数,偶数和偶数之间的相对位置不做要求,但是时间复杂度和空间复杂度必须如下要求。
数据范围:0≤𝑛≤500000≤n ≤50000,数组中每个数的值 0≤𝑣𝑎𝑙≤100000≤va l ≤10000
要求:时间复杂度 𝑂(𝑛),空间复杂度 𝑂(1)
示例1 1 2 输入:[1,2,3,4] 返回值:[1,3,2,4]
说明:
1 [3,1,2,4]或者[3,1,4,2]也是正确答案
解题 方案1: 同JZ21
方案2: 双指针
JZ39 数组中出现次数超过一半的数字 描述 给一个长度为 n 的数组,数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。
例如输入一个长度为9的数组[1,2,3,2,2,2,5,4,2]。由于数字2在数组中出现了5次,超过数组长度的一半,因此输出2。
数据范围:𝑛≤50000n ≤50000,数组中元素的值 0≤𝑣𝑎𝑙≤100000≤va l ≤10000
要求:空间复杂度:𝑂(1)O (1),时间复杂度 𝑂(𝑛)O (n )
输入描述:
保证数组输入非空,且保证有解
案例 1 2 输入:[1,2,3,2,2,2,5,4,2] 返回值:2
解题 方案1: 利用排序
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 class Solution : def MoreThanHalfNum_Solution (self , numbers: List [int ] ) -> int : if not numbers: return -1 if len (numbers)==1 : return numbers[0 ] lenght = len (numbers) / 2 numbers.sort() count = 1 maxlen = 0 res = -1 for idx in range (len (numbers) - 1 ): if maxlen > lenght: return res if numbers[idx] == numbers[idx + 1 ]: count += 1 maxlen = max (maxlen, count) res = numbers[idx + 1 ] else : count = 1 return res
链表 JZ6 从尾到头打印链表 描述 输入一个链表的头节点,按链表从尾到头的顺序返回每个节点的值(用数组返回)。
如输入{1,2,3}的链表如下图:
返回一个数组为[3,2,1]
0 <= 链表长度 <= 10000
示例1
解题 方案1: 顺序存储,倒序输出——>>时间$O(n)$,空间$O(n)$
补充:Java Collections交换Arraylist元素位置
1 2 3 4 5 6 7 8 9 10 11 class Solution : def printListFromTailToHead (self , listNode: ListNode ) -> List [int ]: if not listNode: return [] res = [] while (listNode): res.append(listNode.val) listNode = listNode.next return [res[idx] for idx in range (len (res)-1 , -1 , -1 )]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 import java.util.*;import java.util.ArrayList;public class Solution { public ArrayList<Integer> printListFromTailToHead (ListNode listNode) { ArrayList<Integer> res = new ArrayList <>(); if (listNode == null ){ return res; } while (listNode != null ){ res.add(listNode.val); listNode = listNode.next; } int l = 0 ; for (int i = res.size() - 1 ; i > l; i--){ Collections.swap(res, l++, i); } return res; } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 import java.util.*;import java.util.ArrayList;public class Solution { public ArrayList<Integer> printListFromTailToHead (ListNode listNode) { ArrayList<Integer> res = new ArrayList <>(); if (listNode == null ){ return res; } while (listNode != null ){ res.add(listNode.val); listNode = listNode.next; } return result; } }
方案2:
JZ18 删除链表的节点 描述 给定单向链表的头指针和一个要删除的节点的值,定义一个函数删除该节点。返回删除后的链表的头节点。
1.此题对比原题有改动
2.题目保证链表中节点的值互不相同
3.该题只会输出返回的链表和结果做对比,所以若使用 C 或 C++ 语言,你不需要 free 或 delete 被删除的节点
数据范围:
0<=链表节点值<=10000
0<=链表长度<=10000
示例1 1 2 输入:{2,5,1,9},5 返回值:{2,1,9}
解题 方案1: 遍历查找删除
补充:单链表知识
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 class ListNode : def __init__ (self, x ): self.val = x self.next = None class Solution : def deleteNode (self , head: ListNode, val: int ) -> ListNode: """ :param head: ListNode类 :param val: int整型 :return: 删除val值(节点)后的ListNode """ if not head: return head if head.val == val: return head.next temp = head.next cur = head while temp: if temp.val == val: cur.next = temp.next return head temp = temp.next cur = cur.next return head
方案2: xxxx