01 - 25
3.数组中重复的数字
在一个长度为 n 的数组 nums 里的所有数字都在 0~n-1 的范围内。数组中某些数字是重复的,但不知道有几个数字重复了,也不知道每个数字重复了几次。请找出数组中任意一个重复的数字。
输入: [2, 3, 1, 0, 2, 5, 3] 输出:2 或 3
方法:
哈希表
sort后,进行查找
元素交换到其下标对应位置(最优解)
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 28 29 30 31 int findRepeatNumber (vector <int >& nums) { for (int i = 0 ; i < nums.size (); i++) { if (nums[i] != i) { if (nums[i] == nums[nums[i]]) return nums[i]; else { int temp = nums[i]; nums[i] = nums[temp]; nums[temp] = temp; } } } return -1 ; }
4.二维数组中的查找
在一个 n * m 的二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。
输入:
[
[1, 4, 7, 11, 15],
[2, 5, 8, 12, 19],
[3, 6, 9, 16, 22],
[10, 13, 14, 17, 24],
[18, 21, 23, 26, 30]
]
给定 target = 5,返回 true。
给定 target = 20,返回 false。
方法:
特征:矩阵,从左至右,从上至下依次递增
使用:线性查找,从有右上角开始查询
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 bool findNumberIn2DArray (vector <vector <int >>& matrix, int target) { if (matrix.size () == 0 ) return false ; int m = matrix.size (); int n = matrix[0 ].size (); int row = 0 ; int col = n - 1 ; while (row < m && col >= 0 ) { if (matrix[row][col] == target) return true ; else if (matrix[row][col] > target) col--; else row++; } return false ; }
5.替换空格
请实现一个函数,把字符串 s 中的每个空格替换成”%20”。
输入:
输入:s = “We are happy.”
输出:”We%20are%20happy.”
新的string,遍历后增加替换
string的成员函数replace,A.replace(i_开始的下标 , len_长度 , “替换的字符”)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 string replaceSpace (string s) { if (s.size () == 0 ) return s; for (int i = 0 ; i < s.size (); i++) { if (s[i] == ' ' ) s.replace(i, 1 , "%20" ); } return s; }
6.从头到尾打印链表
输入一个链表的头节点,从尾到头反过来返回每个节点的值(用数组返回)。
输入:
输入:head = [1,3,2]
输出:[2,3,1]
方法:
利用deque
利用vector(倒置初始化)/ reserve
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 vector <int > reversePrint (ListNode* head) { vector <int > deq; while (head != NULL ) { deq.push_back(head->val); head = head->next; } vector <int >arr(deq.rbegin(), deq.rend()); return arr; }
7. 重建二叉树
输入某二叉树的前序遍历和中序遍历的结果,请重建该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。
输入:
前序遍历 preorder = [3,9,20,15,7]
中序遍历 inorder = [9,3,15,20,7]
方法:
反复利用数组的下标初始化
先序——定根
中序——定区间
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 TreeNode* buildTree (vector <int >& preorder, vector <int >& inorder) { return build(preorder, inorder, 0 , 0 , preorder.size () - 1 ); } TreeNode* build (vector <int >& preorder, vector <int >& inorder, int root, int start, int end ) { if (start > end ) return NULL ; TreeNode* tree = new TreeNode(preorder[root]); int i = start; while (i < end &&preorder[root] != inorder[i]) i++; tree->left = build(preorder, inorder, root + 1 , start, i - 1 ); tree->right = build(preorder, inorder, root + 1 + i - start, i + 1 , end ); return tree; }
9.用两个栈实现队列
用两个栈实现一个队列。队列的声明如下,请实现它的两个函数 appendTail 和 deleteHead ,分别完成在队列尾部插入整数和在队列头部删除整数的功能。(若队列中没有元素,deleteHead 操作返回 -1 )
输入:略
利用两个栈
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 stack <int >s1;stack <int >s2;void appendTail (int value) { while (!s1.empty()) { int temp = s1.top(); s2.push(temp); s1.pop(); } s1.push(value); while (!s2.empty()) { int temp = s2.top(); s1.push(temp); s2.pop(); } } int deleteHead () { if (s1.empty()) return -1 ; int temp = s1.top(); s1.pop(); return temp; }
10-1.斐波那契数列
写一个函数,输入 n ,求斐波那契(Fibonacci)数列的第 n 项。斐波那契数列的定义如下:
F(0) = 0, F(1) = 1
F(N) = F(N - 1) + F(N - 2), 其中 N > 1.
输入:n = 2 输出:1
输入:n = 5 输出:5
dp方程
a = a + b、b = a - b
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 int fib (int n) { if (n == 0 )return 0 ; if (n <= 2 )return 1 ; int a = 1 ; int b = 0 ; for (int i = 2 ; i <= n; i++) { a = a + b; b = a - b; a %= 1000000007 ; } return a; }
10-2 .青蛙跳台阶问题
一只青蛙一次可以跳上1级台阶,也可以跳上2级台阶。求该青蛙跳上一个 n 级的台阶总共有多少种跳法。
答案需要取模 1e9+7(1000000007),如计算初始结果为:1000000008,请返回 1。
11. 旋转数组的最小数字
把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。输入一个递增排序的数组的一个旋转,输出旋转数组的最小元素。例如,数组 [3,4,5,1,2] 为 [1,2,3,4,5] 的一个旋转,该数组的最小值为1。
输入:
输入:[3,4,5,1,2]
输出:1
输入:[2,2,2,0,1]
输出:0
解:就是找到最小的那个数,就是导致数组旋转
方法:
二分法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 int minArray (vector <int >& numbers) { if (numbers.size () == 1 )return numbers[0 ]; int L = 0 , R = numbers.size () - 1 ; while (L < R) { int mid = (L + R) / 2 ; if (numbers[mid] < numbers[R]) { R = mid; } else if (numbers[mid] > numbers[R]) { L = mid + 1 ; } else R--; } return numbers[L]; }
12.矩阵中的路径
请设计一个函数,用来判断在一个矩阵中是否存在一条包含某字符串所有字符的路径。路径可以从矩阵中的任意一格开始,每一步可以在矩阵中向左、右、上、下移动一格。如果一条路径经过了矩阵的某一格,那么该路径不能再次进入该格子。例如,在下面的3×4的矩阵中包含一条字符串“bfce”的路径(路径中的字母用加粗标出)。
输入:
[[“a”,“b” ,”c”,”e”],
[“s”,“f” ,“c” ,”s”],
[“a”,”d”,“e” ,”e”]]
“bfce” ,true
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 28 29 30 31 bool exist (vector <vector <char >>& board, string word ) { int len = board.size (); if (len == 0 || board[0 ].size () == 0 || word .empty())return false ; vector <vector <bool >>vis(len, vector <bool >(board[0 ].size (), true )); for (int i = 0 ; i < board.size (); i++) { for (int j = 0 ; j < board[0 ].size (); j++) { if (board[i][j] == word [0 ]) { if (dfs(i, j, board, word , 0 , vis)) return true ; } } } return false ; } bool dfs (int i, int j, vector <vector <char >>& board, string word , int length, vector <vector <bool >>&vis) { if (length == word .size ()) return true ; if (i < 0 || i >= board.size () || j < 0 || j >= board[0 ].size () || vis[i][j] == false || board[i][j] != word [length]) return false ; vis[i][j] = false ; bool res = dfs(i, j + 1 , board, word , length + 1 , vis) || dfs(i, j - 1 , board, word , length + 1 , vis) || dfs(i + 1 , j, board, word , length + 1 , vis) || dfs(i - 1 , j, board, word , length + 1 , vis); vis[i][j] = true ; return res; }
13. 机器人的运动范围
地上有一个m行n列的方格,从坐标 [0,0] 到坐标 [m-1,n-1] 。一个机器人从坐标 [0, 0] 的格子开始移动,它每次可以向左、右、上、下移动一格(不能移动到方格外),也不能进入行坐标和列坐标的数位之和大于k的格子。例如,当k为18时,机器人能够进入方格 [35, 37] ,因为3+5+3+7=18。但它不能进入方格 [35, 38],因为3+5+3+8=19。请问该机器人能够到达多少个格子?
输入: 输入:m = 2, n = 3, k = 1 输出:3
输入:m = 3, n = 1, k = 0 输出:1
典型的BFS,但是只有两个方向需要注意
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 28 29 30 31 32 33 34 35 36 37 38 int get (int x) { int res = 0 ; while (x) { res += x % 10 ; x /= 10 ; } return res; } int movingCount (int m, int n, int k) { if (!k) return 1 ; vector <vector <int >>vis(m, vector <int >(n, 0 )); vis[0 ][0 ] = 1 ; queue <pair<int , int >> Q; Q.push(make_pair(0 , 0 )); int dx[2 ] = { 0 , 1 }; int dy[2 ] = { 1 , 0 }; int ans = 1 ; while (!Q.empty()) { auto [x, y] = Q.front(); Q.pop(); for (int i = 0 ; i < 2 ; ++i) { int tx = dx[i] + x; int ty = dy[i] + y; if (tx >= 0 && tx < m && ty >= 0 && ty < n && get (tx) + get (ty) < k + 1 && vis[tx][ty] == 0 ) { Q.push(make_pair(tx, ty)); vis[tx][ty] = 1 ; ans++; } } } return ans; }
14-1.剪绳子
给你一根长度为 n 的绳子,请把绳子剪成整数长度的 m 段(m、n都是整数,n>1并且m>1),每段绳子的长度记为 k[0],k[1]…k[m-1] 。请问 k[0]k[1] …*k[m-1] 可能的最大乘积是多少?例如,当绳子的长度是8时,我们把它剪成长度分别为2、3、3的三段,此时得到的最大乘积是18。
输入: 输入: 2 输出: 1 解释: 2 = 1 + 1, 1 × 1 = 1
输入: 10 输出: 36 解释: 10 = 3 + 3 + 4, 3 × 3 × 4 = 36
解:只有绳子大于4的时候,才需要剪开可以乘积变大
1 / 1 0
2 / 1*1=1
3 / 1*2=2
4 / 2*2=4
5 / 2*3=5 //开始变化
方法:
找规律
dp动态规划
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 int cuttingRope (int n) { if (n < 4 )return n - 1 ; if (n == 4 )return 4 ; vector <int > dp (n + 1 ) ; dp[1 ] = 1 ; dp[2 ] = 2 ; dp[3 ] = 3 ; dp[4 ] = 4 ; for (int i = 5 ; i <= n; i++) { int maxNum = 0 ; for (int j = 1 ; j <= i / 2 ; j++) { maxNum = max (maxNum, dp[i - j] * dp[j]); } dp[i] = maxNum; } return dp[n]; }
14-2.剪绳子 II
15.二进制中1的个数 1 2 3 4 5 6 7 8 9 int hammingWeight (uint32_t n) { int res = 0 ; while (n) { if (n & 1 ) res++; n = n >> 1 ; } return res; }
16.数值的整数次方
实现函数double Power(double base, int exponent),求base的exponent次方。不得使用库函数,同时不需要考虑大数问题。
输入: 2.00000, 10 输出: 1024.00000
输入: 2.10000, 3 输出: 9.26100
输入: 2.00000, -2 输出: 0.25000 解释: 2-2 = 1/22 = 1/4 = 0.25
dfs————拆分拆分 / 化繁为简
1 2 3 4 5 6 7 8 9 double myPow (double x, int n) { if (n == 0 ) return 1 ; if (n == 1 ) return x; if (n == -1 ) return 1 / x; double half = myPow(x, n / 2 ); double add = myPow(x, n % 2 ); return half * half*add; }
17.打印从1到最大的n位数
输入数字 n,按顺序打印出从 1 到最大的 n 位十进制数。比如输入 3,则打印出 1、2、3 一直到最大的 3 位数 999。
输入: 输入: n = 1 输出: [1,2,3,4,5,6,7,8,9]
主要考大数问题,用字符串模拟加法 到了这,这题变得毫无意义
方法:
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 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 vector <int > res;vector <int > printNumbers (int n) { if (n <= 0 ) return res; string number (n, '0' ) ; for (int i = 0 ; i <= 9 ; i++) { number[0 ] = i + '0' ; permutionNum(number, n, 1 ); } return res; } void permutionNum (string &number, int length, int index) { if (index == length) { saveNum(number); return ; } else { for (int i = 0 ; i < 10 ; i++) { number[index] = '0' + i; permutionNum(number, length, index + 1 ); } } } void saveNum (string number) { string tempStr (number.size (), '0' ) ; std ::cout << stoi(number) << " " ; if (number != tempStr) res.push_back(stoi(number)); } void bigAdd (string num1, string num2, string &res) { int carry = 0 ; int len1 = num1.length() - 1 ; int len2 = num2.length() - 1 ; res = "" ; while (len1 >= 0 || len2 >= 0 ) { int a = len1 >= 0 ? num1[len1--] - '0' : 0 ; int b = len2 >= 0 ? num2[len2--] - '0' : 0 ; int tmp = a + b + carry; carry = tmp / 10 ; res = to_string(long long (tmp % 10 )) + res; } if (carry) { res = to_string(long long (carry)) + res; } }
18.删除链表的节点
给定单向链表的头指针和一个要删除的节点的值,定义一个函数删除该节点。
返回删除后的链表的头节点。
输入:
输入: head = [4,5,1,9], val = 5
输出: [4,1,9]
解释: 给定你链表中值为 5 的第二个节点,那么在调用了你的函数之后,该链表应变为 4 -> 1 -> 9.
输入: head = [4,5,1,9], val = 1
输出: [4,5,9]
解释: 给定你链表中值为 1 的第三个节点,那么在调用了你的函数之后,该链表应变为 4 -> 5 -> 9.
方法:
原题剑指1:给定头节点和要删除的节点(快速删除)
删除要删除节点的下一个节点,将下一个节点的值赋给前一个结点
当最后一个节点为空,还是需要从头遍历
原题剑指2:删除链表中的重复节点
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 ListNode* deleteNode (ListNode* head, int val) { if (head == NULL ) return head; if (head->val == val) return head->next; ListNode* pre = head; ListNode* temp = NULL ; while (head->next && head) { temp = head; head = head->next; if (head->val == val) { temp->next = head->next; break ; } } return pre; }
19. 正则表达式匹配
请实现一个函数用来匹配包含’. ‘和’‘的正则表达式。模式中的字符’.’表示任意一个字符,而’ ‘表示它前面的字符可以出现任意次(含0次)。在本题中,匹配是指字符串的所有字符匹配整个模式。例如,字符串”aaa”与模式”a.a”和”abac a”匹配,但与”aa.a”和”ab*a”均不匹配。
1 2 3 4 5 6 7 8 9 10 bool isMatch (string s, string p) { if (p.empty()) return s.empty(); if (p[1 ] == '*' ) { return isMatch(s, p.substr(2 )) || (!s.empty() && (s[0 ] == p[0 ] || p[0 ] == '.' )) && isMatch(s.substr(1 ), p); } else { return !s.empty() && (s[0 ] == p[0 ] || p[0 ] == '.' ) && (isMatch(s.substr(1 ), p.substr(1 ))); } }
20.表示数值的字符串
哈希表
sort后,进行查找
21.调整数组顺序使奇数位于偶数前面
输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有奇数位于数组的前半部分,所有偶数位于数组的后半部分。
输入:
输入:nums = [1,2,3,4]
输出:[1,3,2,4]
注:[3,1,2,4] 也是正确的答案之一。
一次快排
快排需要注意:while右边先写
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 vector <int > exchange (vector <int >& nums) { if (nums.size () == 0 ) return {}; int L = 0 ; int R = nums.size () - 1 ; while (L != R) { while (L < R &&nums[L] % 2 == 1 ) L++; while (L < R &&nums[R] % 2 == 0 ) R--; if (L < R) { int temp = nums[R]; nums[R] = nums[L]; nums[L] = temp; } } return nums; }
22.链表中倒数第k个节点
输入一个链表,输出该链表中倒数第k个节点。为了符合大多数人的习惯,本题从1开始计数,即链表的尾节点是倒数第1个节点。例如,一个链表有6个节点,从头节点开始,它们的值依次是1、2、3、4、5、6。这个链表的倒数第3个节点是值为4的节点。
vector保存所有结点
快慢指针,快指针先走K,然后慢指针开始走,sum - k = 要走的步数。
1 2 3 4 5 6 7 8 9 10 11 12 13 ListNode* getKthFromEnd (ListNode* head, int k) { ListNode* fast = head; ListNode* slow = head; while (k && fast) { fast = fast->next; k--; } while (fast) { fast = fast->next; slow = slow->next; } return slow; }
24.反转链表
1.递归遍历到最后一个节点开始反转
2.辅助指针逐步反转
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 28 29 30 ListNode* reverseList (ListNode* head) { if (head == NULL || head->next == NULL ) return head; ListNode* temp = head->next; ListNode* newH = reverseList(temp); temp->next = head; head->next = NULL ; return newH; }
25.合并两个排序的链表
输入两个递增排序的链表,合并这两个链表并使新链表中的节点仍然是递增排序的。
输入:
输入:1->2->4, 1->3->4
输出:1->1->2->3->4->4
方法:
类似归并排序,归的时候,逐步组成一个有序集合
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 28 29 30 31 32 33 ListNode* mergeTwoLists (ListNode* l1, ListNode* l2) { ListNode* pre = NULL ; ListNode* head = NULL ; if (l1 == NULL )return l2; if (l2 == NULL )return l1; if (l1->val <= l2->val) { pre = l1; l1 = l1->next; } else { pre = l2; l2 = l2->next; } head = pre; while (l1&&l2) { if (l1->val <= l2->val) { pre->next = l1; pre = pre->next; l1 = l1->next; } else { pre->next = l2; pre = pre->next; l2 = l2->next; } } if (l1) pre->next = l1; if (l2) pre->next = l2; return head; }