日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

算法工程师笔试 -剑指offer-习题详细解答

發(fā)布時間:2024/7/5 编程问答 43 豆豆
生活随笔 收集整理的這篇文章主要介紹了 算法工程师笔试 -剑指offer-习题详细解答 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

說明

  • 主要編程語言為 C/C++
  • 涉及字符串的問題可能會使用 Python
  • 題目編號以原書為準(zhǔn),如“面試題 3:數(shù)組中重復(fù)的數(shù)字
    • 因為題目不多,所以就不做分類了
  • 所有代碼均通過 OJ 測試

    在線 OJ 地址:劍指Offer_編程題 - 牛客網(wǎng)

Reference

  • 《劍指 Offer(第二版)》 - 何海濤
  • Interview-Notebook/劍指 offer 題解.md · CyC2018/Interview-Notebook
  • 牛客網(wǎng)相關(guān)問題討論區(qū)

3.1 數(shù)組中重復(fù)的數(shù)字

數(shù)組中重復(fù)的數(shù)字 - NowCoder

題目描述

在一個長度為 n 的數(shù)組里的所有數(shù)字都在 0 到 n-1 的范圍內(nèi)。 數(shù)組中某些數(shù)字是重復(fù)的,但不知道有幾個數(shù)字是重復(fù)的,也不知道每個數(shù)字重復(fù)幾次。 請找出數(shù)組中任意一個重復(fù)的數(shù)字。
  • 要求:時間復(fù)雜度O(N),空間復(fù)雜度O(1)
  • 示例Input: {2, 3, 1, 0, 2, 5}Output: 2

思路

  • 復(fù)雜度要求表明不能使用排序,也不能使用 map/set
  • 注意到 n 個數(shù)字的范圍為 0 到 n-1,考慮類似選擇排序的思路,通過一次遍歷將每個數(shù)交換到排序后的位置,如果該位置已經(jīng)存在相同的數(shù)字,那么該數(shù)就是重復(fù)的
  • 示例position-0 : (2,3,1,0,2,5) // 2 <-> 1(1,3,2,0,2,5) // 1 <-> 3(3,1,2,0,2,5) // 3 <-> 0(0,1,2,3,2,5) // already in position position-1 : (0,1,2,3,2,5) // already in position position-2 : (0,1,2,3,2,5) // already in position position-3 : (0,1,2,3,2,5) // already in position position-4 : (0,1,2,3,2,5) // nums[i] == nums[nums[i]], exit

Code

class Solution { public:bool duplicate(int numbers[], int length, int* duplication) {if(numbers == nullptr || length <= 0)return false;for(int i = 0; i < length; ++i) {while(numbers[i] != i) {if(numbers[i] == numbers[numbers[i]]) {*duplication = numbers[i];return true;}// 交換numbers[i]和numbers[numbers[i]]swap(numbers[i], numbers[numbers[i]]);}}return false;} };

3.2 不修改數(shù)組找出重復(fù)的數(shù)字

題目描述

在一個長度為n+1的數(shù)組里的所有數(shù)字都在1到n的范圍內(nèi),所以數(shù)組中至少有一個數(shù)字是重復(fù)的。 請找出數(shù)組中任意一個重復(fù)的數(shù)字,但不能修改輸入的數(shù)組。 例如,如果輸入長度為8的數(shù)組{2, 3, 5, 4, 3, 2, 6, 7},那么對應(yīng)的輸出是重復(fù)的數(shù)字2或者3。
  • 要求:時間復(fù)雜度O(NlogN),空間復(fù)雜度O(1)

思路

  • 二分查找
  • 以長度為 8 的數(shù)組 {2, 3, 5, 4, 3, 2, 6, 7} 為例,那么所有數(shù)字都在 1~7 的范圍內(nèi)。中間的數(shù)字 4 將 1~7 分為 1~4 和 5~7。統(tǒng)計 1~4 內(nèi)數(shù)字的出現(xiàn)次數(shù),它們一共出現(xiàn)了 5 次,說明 1~4 內(nèi)必要重復(fù)的數(shù)字;反之,若小于等于 4 次,則說明 5~7 內(nèi)必有重復(fù)的數(shù)字。
  • 因為不能使用額外的空間,所以每次統(tǒng)計次數(shù)都要重新遍歷整個數(shù)組一次

Code

int countRange(const int* numbers, int length, int start, int end);int getDuplication(const int* numbers, int length) {if(numbers == nullptr || length <= 0)return -1;int start = 1;int end = length - 1;while(end >= start) {int middle = ((end - start) >> 1) + start;int count = countRange(numbers, length, start, middle);if(end == start) {if(count > 1)return start;elsebreak;}if(count > (middle - start + 1))end = middle;elsestart = middle + 1;}return -1; }// 因為不能使用額外的空間,所以每次統(tǒng)計次數(shù)都要重新遍歷整個數(shù)組一次 int countRange(const int* numbers, int length, int start, int end) {if(numbers == nullptr)return 0;int count = 0;for(int i = 0; i < length; i++)if(numbers[i] >= start && numbers[i] <= end)++count;return count; }

4. 二維數(shù)組中的查找

二維數(shù)組中的查找 - NowCoder

題目描述

在一個二維數(shù)組中,每一行都按照從左到右遞增的順序排序,每一列都按照從上到下遞增的順序排序。 請完成一個函數(shù),輸入這樣的一個二維數(shù)組和一個整數(shù),判斷數(shù)組中是否含有該整數(shù)。
  • 示例Consider the following matrix: [[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] ]Given target = 5, return true. Given target = 20, return false.

思路

  • 左下角開始查找,它左邊的數(shù)都比它小,下邊的數(shù)都比它大;因此可以根據(jù) target 和當(dāng)前元素的大小關(guān)系來縮小查找區(qū)間
  • 同理,也可以從右上角開始查找
  • 時間復(fù)雜度:O(M + N)

Code

class Solution { public:bool Find(int target, vector<vector<int> > array) {int N = array.size(); // 行數(shù)int M = array[0].size(); // 列數(shù)int i = N - 1;int j = 0;while (i >= 0 && j < M) {if (array[i][j] > target)i--;else if (array[i][j] < target)j++;elsereturn true;}return false;} };

5. 替換空格

替換空格 - NowCoder

題目描述

請實現(xiàn)一個函數(shù),將一個字符串中的空格替換成“%20”。 例如,當(dāng)字符串為 "We Are Happy". 則經(jīng)過替換之后的字符串為 "We%20Are%20Happy"。

思路

  • 先遍歷一次,找出空格的數(shù)量,得到替換后的長度;然后從后往前替換

Code

class Solution { public:void replaceSpace(char *str, int length) {if (str == nullptr || length < 0)return;int l_old = strlen(str); // == lengthint n_space = count(str, str + l_old, ' '); // <algorithm>int l_new = l_old + n_space * 2;str[l_new] = '\0';int p_old = l_old-1;int p_new = l_new-1;while (p_old >= 0) {if (str[p_old] != ' ') {str[p_new--] = str[p_old--];}else {p_old--;str[p_new--] = '0';str[p_new--] = '2';str[p_new--] = '%';}}} };

6. 從尾到頭打印鏈表

從尾到頭打印鏈表 - NowCoder

題目描述

輸入鏈表的第一個節(jié)點,從尾到頭反過來打印出每個結(jié)點的值。

思路

  • 頭插法

Code

class Solution { public:vector<int> printListFromTailToHead(ListNode* head) {vector<int> ret;ListNode *p = head;while (p != NULL) {ret.insert(ret.begin(), p->val); // 頭插p = p->next;}return ret;} };

7. 重建二叉樹

重建二叉樹 - NowCoder

題目描述

根據(jù)二叉樹的前序遍歷和中序遍歷的結(jié)果,重建出該二叉樹。 假設(shè)輸入的前序遍歷和中序遍歷的結(jié)果中都不含重復(fù)的數(shù)字。

思路

  • 涉及二叉樹的問題,應(yīng)該條件反射般的使用遞歸(無優(yōu)化要求時)
  • 前序遍歷的第一個值為根節(jié)點的值,使用這個值將中序遍歷結(jié)果分成兩部分,左部分為左子樹的中序遍歷結(jié)果,右部分為右子樹的中序遍歷的結(jié)果。

Code - 無優(yōu)化

struct TreeNode {int val;TreeNode *left;TreeNode *right;TreeNode(int x) : val(x), left(NULL), right(NULL) {} };class Solution { public:TreeNode * reConstructBinaryTree(vector<int> pre, vector<int> vin) {if (pre.size() <= 0)return NULL;TreeNode* root = new TreeNode{ pre[0] };for (auto i = 0; i < vin.size(); i++) {if (vin[i] == pre[0]) {root->left = reConstructBinaryTree(vector<int>(pre.begin() + 1, pre.begin() + 1 + i), vector<int>(vin.begin(), vin.begin() + i));root->right = reConstructBinaryTree(vector<int>(pre.begin() + 1 + i, pre.end()), vector<int>(vin.begin() + 1 + i, vin.end()));}}return root;} };

Code - 優(yōu)化

class Solution { public:TreeNode * reConstructBinaryTree(vector<int> pre, vector<int> vin) {return reConstructCore(pre, 0, pre.size(), vin, 0, vin.size());}TreeNode * reConstructCore(vector<int> &pre, int pre_beg, int pre_end, vector<int> &vin, int vin_beg, int vin_end) {if (pre_end - pre_beg <= 0)return NULL;TreeNode* root = new TreeNode{ pre[pre_beg] };for (auto i = 0; i < vin_end-vin_beg; i++) {if (vin[i+vin_beg] == pre[pre_beg]) {root->left = reConstructCore(pre, pre_beg+1, pre_beg+1+i, vin, vin_beg, vin_beg+i);root->right = reConstructCore(pre, pre_beg+1+i, pre_end, vin, vin_beg+1+i, vin_end);}}return root;} };

8. 二叉樹的下一個結(jié)點

二叉樹的下一個結(jié)點 - NowCoder

題目描述

給定一個二叉樹和其中的一個結(jié)點,請找出中序遍歷順序的下一個結(jié)點并且返回。 注意,樹中的結(jié)點不僅包含左右子結(jié)點,同時包含指向父結(jié)點的指針。

思路

  • 回顧中序遍歷的順序
  • 如果一個節(jié)點的右子樹不為空,那么下一個節(jié)點是該節(jié)點右子樹的最左葉子;
  • 否則(右子樹為空),沿父節(jié)點向上直到找到某個節(jié)點是其父節(jié)點的左孩子,那么該父節(jié)點就是下一個節(jié)點

Code

struct TreeLinkNode {int val;struct TreeLinkNode *left;struct TreeLinkNode *right;struct TreeLinkNode *next;TreeLinkNode(int x) :val(x), left(NULL), right(NULL), next(NULL) {} };class Solution { public:TreeLinkNode * GetNext(TreeLinkNode* pNode) {if (pNode == nullptr)return nullptr;if(pNode->right != nullptr) {auto p = pNode->right;while(p->left != nullptr)p = p->left;return p;}else {auto p = pNode; // 當(dāng)前節(jié)點while(p->next != nullptr) { // 當(dāng)前節(jié)點的父節(jié)點不為空if (p->next->left == p) // 當(dāng)前節(jié)點是其父節(jié)點的左海子return p->next; // 那么下一個節(jié)點就是當(dāng)前節(jié)點的父節(jié)點p = p->next;}}return nullptr; // 當(dāng)前節(jié)點是根節(jié)點且沒有右孩子,即沒有下一個節(jié)點} };

9. 用兩個棧實現(xiàn)隊列

用兩個棧實現(xiàn)隊列 - NowCoder

題目描述

用兩個棧來實現(xiàn)一個隊列,完成隊列的 Push 和 Pop 操作。

思路

  • 假設(shè) stack_in 用于處理入棧操作,stack_out用于處理出棧操作
  • stack_in 按棧的方式正常處理入棧數(shù)據(jù);
  • 關(guān)鍵在于出棧操作
    • 當(dāng)stack_out為空時,需要先將每個stack_in中的數(shù)據(jù)出棧后壓入stack_out
    • 反之,每次彈出stack_out棧頂元素即可

Code

class Solution { public:void push(int node) {stack_in.push(node);}int pop() {if(stack_out.size() <= 0) {while (stack_in.size() > 0) {auto tmp = stack_in.top();stack_in.pop();stack_out.push(tmp);}}auto ret = stack_out.top();stack_out.pop();return ret;}private:stack<int> stack_in;stack<int> stack_out; };

10.1 斐波那契數(shù)列

斐波那契數(shù)列 - NowCoder

題目描述

寫一個函數(shù),輸入n,求斐波那契(Fibonacci)數(shù)列的第n項。 數(shù)列的前兩項為 0 和 1

思路

  • 遞歸
    • 遞歸可能會重復(fù)計算子問題——例如,計算 f(10) 需要計算 f(9) 和 f(8),計算 f(9) 需要計算 f(8) 和 f(7),可以看到 f(8) 被重復(fù)計算了
    • 可以利用額外空間將計算過的子問題存起來
  • 查表
    • 因為只需要前 40 項,所以可以先將值都求出來

Code - 遞歸

// 該代碼會因復(fù)雜度過大無法通過評測 class Solution { public:int Fibonacci(int n) {if(n <= 0)return 0;if(n == 1)return 1;return Fibonacci(n - 1) + Fibonacci(n - 2);} };

Code - 循環(huán)

class Solution { public:int Fibonacci(int n) {int f = 0;int g = 1;while (n--) {g = g + f;f = g - f;}return f;} };

Code - 查表

class Solution { public:Solution(){fib = new int[40];fib[0] = 0;fib[1] = 1;for (int i = 2; i < 40; i++)fib[i] = fib[i - 1] + fib[i - 2];}int Fibonacci(int n) {return fib[n];}private:int* fib; };

10.2 跳臺階(遞歸)

跳臺階 | 變態(tài)跳臺階 - NowCoder

題目描述

一只青蛙一次可以跳上1級臺階,也可以跳上2級。 求該青蛙跳上一個n級的臺階總共有多少種跳法(先后次序不同算不同的結(jié)果)。

思路

  • 遞歸
  • 記跳 n 級臺階有 f(n) 種方法
    • 如果第一次跳 1 級,那么之后的 n-1 級有 f(n-1) 種跳法
    • 如果第一次跳 2 級,那么之后的 n-2 級有 f(n-2) 種跳法
  • 實際上就是首兩項為 1 和 2 的斐波那契數(shù)列

    Code
class Solution { public:int jumpFloor(int number) {int f = 1;int g = 2;number--;while (number--) {g = g + f;f = g - f;}return f;} };

10.3 變態(tài)跳臺階(動態(tài)規(guī)劃)

變態(tài)跳臺階 - NowCoder

題目描述

一只青蛙一次可以跳上1級臺階,也可以跳上2級……它也可以跳上n級。 求該青蛙跳上一個n級的臺階總共有多少種跳法。

思路

  • 動態(tài)規(guī)劃
  • 遞推公式f(1) = 1 f(n) = 1 + f(1) + .. + f(n-1)

Code - DP

class Solution { public:int jumpFloorII(int number) {vector<int> dp(number+1, 1);for (int i=2; i<=number; i++)for(int j=1; j<i; j++)dp[i] += dp[j];return dp[number];} };

Code - 空間優(yōu)化

class Solution { public:int jumpFloorII(int number) {int f = 1;int sum = 1 + f;for (int i = 2; i <= number; i++) {f = sum;sum += f;}return f;} };

10.4 矩形覆蓋(動態(tài)規(guī)劃)

矩形覆蓋 - NowCoder

題目描述

我們可以用2*1的小矩形橫著或者豎著去覆蓋更大的矩形。 請問用n個2*1的小矩形無重疊地覆蓋一個2*n的大矩形,總共有多少種方法?

思路

  • 動態(tài)規(guī)劃
  • 遞推公式f(1) = 1 f(2) = 2 f(n) = f(n-1) + f(n-2)
  • 即前兩項為 1 和 2 的斐波那契數(shù)列

Code

class Solution { public:int rectCover(int number) {if (number == 0)return 0;int f = 1;int g = 2;for (int i = 2; i <= number; i++) {g = g + f;f = g - f;}return f;} };

11. 旋轉(zhuǎn)數(shù)組的最小數(shù)字(二分查找)

旋轉(zhuǎn)數(shù)組的最小數(shù)字 - NowCoder

題目描述

把一個數(shù)組最開始的若干個元素搬到數(shù)組的末尾,我們稱之為數(shù)組的旋轉(zhuǎn)。 輸入一個非遞減排序的數(shù)組的一個旋轉(zhuǎn),輸出旋轉(zhuǎn)數(shù)組的最小元素。例如數(shù)組 {3, 4, 5, 1, 2} 為 {1, 2, 3, 4, 5} 的一個旋轉(zhuǎn),該數(shù)組的最小值為 1。 NOTE:給出的所有元素都大于 0,若數(shù)組大小為 0,請返回 0。

思路

  • 二分查找
  • 二分查找需要有一個目標(biāo)值 target,這里的 target 可以選 nums[hi] 或 nums[lo],這里使用過的是 nums[hi]
  • 注意有重復(fù)的情況,特別是 {3, 4, 5, 1, 2, 3},這里有一個簡單的處理方法

Code

class Solution { public:int minNumberInRotateArray(vector<int> rotateArray) {if (rotateArray.empty())return 0;int lo = 0;int hi = rotateArray.size() - 1;// 完全旋轉(zhuǎn),或者說沒有旋轉(zhuǎn)(不需要)//if (rotateArray[lo] < rotateArray[hi])// return rotateArray[lo];while (lo + 1 < hi) {int mid = lo + (hi - lo) / 2;if (rotateArray[mid] > rotateArray[hi])lo = mid;else if (rotateArray[mid] < rotateArray[hi])hi = mid;elsehi--; // 防止這種情況 {3,4,5,1,2,3}}return rotateArray[hi];} };

12. 矩陣中的路徑(DFS)

矩陣中的路徑 - NowCoder

題目描述

請設(shè)計一個函數(shù),用來判斷在一個矩陣中是否存在一條包含某字符串所有字符的路徑。路徑可以從矩陣中的任意一個格子開始,每一步可以在矩陣中向左,向右,向上,向下移動一個格子。如果一條路徑經(jīng)過了矩陣中的某一個格子,則該路徑不能再進入該格子。
  • 例如下面的矩陣包含了一條 bfce 路徑。

思路

  • 深度優(yōu)先搜索(DFS)
  • 注意邊界判斷

Code

class Solution { public:bool hasPath(char* matrix, int rows, int cols, char* str) {bool *visited = new bool[rows * cols]{false};for (int i=0; i<rows; i++) {for (int j=0; j<cols; j++) {if (dfs(matrix, rows, cols, str, visited, i, j, 0))return true;}}return false;}bool dfs(char* matrix, int rows, int cols, char* str, bool* visited, int i, int j, int step) { // l 為當(dāng)前已找到的長度// 結(jié)果存在if(step == strlen(str))return true;// 邊界條件if(i<0 || i>=rows || j<0 || j>cols || matrix[i*cols+j]!=str[step] || visited[i*cols+j])return false;// 定義 4 個方向;int next[][2] = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}}; // 最好的做法是作為成員變量,但是 C++ 成員變量的初始化比較麻煩,// 而且這還是個二維數(shù)組,更麻煩,所以每次都重新定義一次,所幸不大visited[i*cols+j] = true; // 訪問標(biāo)記for (auto k : next)if (dfs(matrix, rows, cols, str, visited, i+k[0], j+k[1], step+1))return true;visited[i*cols+j] = false; // 清除訪問標(biāo)記return false;} };

13. 機器人的運動范圍(DFS) TODO

機器人的運動范圍 - NowCoder

題目描述

地上有一個 m 行和 n 列的方格。 一個機器人從坐標(biāo) (0, 0) 的格子開始移動,每一次只能向左右上下四個方向移動一格, 但是不能進入行坐標(biāo)和列坐標(biāo)的數(shù)位之和大于 k 的格子。例如,當(dāng) k 為 18 時,機器人能夠進入方格(35, 37),因為 3+5+3+7=18。 但是,它不能進入方格(35, 38),因為 3+5+3+8=19。請問該機器人能夠達到多少個格子?

思路

  • 深度優(yōu)先搜索(DFS)
  • 注意邊界條件判斷

Code

14. 剪繩子(動態(tài)規(guī)劃 | 貪心)

整數(shù)拆分 - LeetCode

題目描述

把一根長度為 n 的繩子剪成 m 段,并且使得每段的長度的乘積最大(n, m 均為整數(shù))。

思路

  • 動態(tài)規(guī)劃
    • 遞推公式

      f(n) = 0 n = 1 f(n) = 1 n = 2 f(n) = 2 n = 3 f(n) = max{dp(i) * dp(n-i)} n > 3, 1<=i<=n-1
    • 注意:當(dāng) n <= 3 時因為必須剪至少一次的緣故,導(dǎo)致 f(1)=0, f(2)=1*1=1, f(3)=1*2=2;但是當(dāng) n>=4 時,將n<=3的部分單獨作為一段能提供更大的乘積

      因此,初始化時應(yīng)該 dp[1]=1≠f(1), dp[2]=2≠f(2), dp[3]=3≠f(3),同時將 f(1), f(2), f(3) 單獨返回

    • 時間復(fù)雜度:O(N^2),空間復(fù)雜度:O(N)

  • 貪心
    • 當(dāng) n>=5 時,盡可能多剪長度為 3 的繩子;當(dāng) n=4 時,剪成兩段長度為 2 的繩子
    • 證明當(dāng) n >= 5 時,可以證明: 3(n-3) > 2(n-2) > n 當(dāng) n == 4 時,2*2 > 3*1
    • 時間復(fù)雜度:O(1),空間復(fù)雜度:O(1)

Code - 動態(tài)規(guī)劃

class Solution { public:int integerBreak(int n) {if(n < 2) return 0;if(n == 2) return 1;if(n == 3) return 2;int dp[n+1]{0}; // 記得初始化為 0dp[1] = 1;dp[2] = 2;dp[3] = 3;for (int i=4; i<=n; i++) {for (int j=1; j<=i/2; j++) {int p = dp[j] * dp[i-j];if (dp[i] < p)dp[i] = p;}}return dp[n];}};

Code - 貪心

class Solution { public:int integerBreak(int n) {if(n < 2) return 0;if(n == 2) return 1;if(n == 3) return 2;int n3 = n / 3; // 切成 3 的數(shù)量if (n%3 == 1) // 如果余下的長度為 4n3--;int n2 = (n - 3*n3) / 2; // 切成 2 的數(shù)量return (int)pow(3, n3) * (int)pow(2, n2);} };

15. 二進制中 1 的個數(shù)(位運算)

二進制中1的個數(shù) - NowCoder

題目描述

輸入一個整數(shù),輸出該數(shù)二進制表示中1的個數(shù)。 其中負數(shù)用補碼表示。

思路

  • 位運算 - 移位計數(shù)
    • 時間復(fù)雜度:O(N),N 為整型的二進制長度
    • 注意移位判斷有兩種方式:一是移動 n,一是移動"1",后者更好
    • 當(dāng) n 為 負數(shù)時,移動 n 可能導(dǎo)致死循環(huán)
  • 位運算 - 利用 n&(n-1)
    • 該運算的效果是每次除去 n 的二進制表示中最后一個 1n : 10110100 n-1 : 10110011 n&(n-1) : 10110000
    • 時間復(fù)雜度:O(M),M 為二進制中 1 的個數(shù)

Code - 移位計數(shù)

class Solution { public:int NumberOf1(int n) {int ret = 0;int N = sizeof(int) * 8;while(N--) {if(n & 1)ret++;n >>= 1;}return ret;} };

Code - 移位計數(shù)(改進)

class Solution { public:int NumberOf1(int n) {int ret = 0;int N = sizeof(int) * 8;int flag = 1;while(N--) {if(n & flag)ret++;flag <<= 1; // 移動 1 而不是 n}return ret;} };

Code - n&(n-1)

class Solution { public:int NumberOf1(int n) {int ret = 0;while(n) {ret++;n = (n-1)&n;}return ret;} };

16. 數(shù)值的整數(shù)次方(位運算)

數(shù)值的整數(shù)次方 - NowCoder

題目描述

給定一個double類型的浮點數(shù)base和int類型的整數(shù)exponent。求base的exponent次方。

思路

  • 位運算 - 快速冪

  • 示例

    求 `3^20 = 9^10 = 81^5 (= 81*81^4) = 81*6561^2 = 81*43046721` 循環(huán)次數(shù) = `bin(20)`的位數(shù) = `len(10100)` = 5
  • 時間復(fù)雜度 O(logN)

Code

class Solution { public:double Power(double base, int exponent) {int p = abs(exponent);double ret = 1.0;while (p != 0) {if (p & 1) // 如果是奇數(shù)ret *= base;base *= base;p >>= 1;}return exponent < 0 ? 1 / ret : ret;} };

17. 打印從 1 到最大的 n 位數(shù)(字符串 + DFS)

題目描述

輸入數(shù)字 n,按順序打印出從 1 到最大的 n 位十進制數(shù)。 比如輸入 3,則打印出 1、2、3 一直到最大的 3 位數(shù)即 999。

思路

  • 由于 n 可能會非常大,因此不能直接用 int 表示數(shù)字,包括 long, long long
  • 正確的做法是用 char 數(shù)組進行存儲。
  • 由于使用 char 存儲數(shù)字,那么就不適合使用普通的運算操作了,此時可以使用 DFS 來獲取所有的數(shù)字

Code

void printOneToMax(int n) {if (n <= 0) return;char* number = new char[n + 1];number[n] = '\0';dfs(number, n, 0); // DFSdelete[] number; }void dfs(char* number, int length, int index) {if (index == length) { // 遞歸最重要的就是結(jié)束條件要正確PrintNumber(number);return;}for (int i = 0; i < 10; ++i) {number[index] = i + '0';dfs(number, length, index + 1);} }// 打印出這個數(shù)字,忽略開頭的 0 void PrintNumber(char* number) {bool isBeginning0 = true;int nLength = strlen(number);for (int i = 0; i < nLength; ++i) {if (isBeginning0 && number[i] != '0')isBeginning0 = false;if (!isBeginning0) {printf("%c", number[i]);}}printf("\t"); }

18.1 在 O(1) 時間內(nèi)刪除鏈表節(jié)點(鏈表)

題目描述

給定單向鏈表的頭指針和需要刪除的指針,定義一個函數(shù)在 O(1) 時間內(nèi)刪除該節(jié)點 前提:該節(jié)點在鏈表中

思路

  • 因為不能遍歷,所以只能通過修改節(jié)點的值來實現(xiàn)這個操作

  • 簡單來說,就是將該節(jié)點的值修改為其下一個節(jié)點的值,實際上刪除的是該節(jié)點的下一個節(jié)點(題目的描述可能會帶來誤導(dǎo))

  • 如果該節(jié)點不是尾節(jié)點,那么按上述操作即可——時間的復(fù)雜度為 O(1)

  • 如果該節(jié)點是尾節(jié)點,此時必須通過遍歷來找到該節(jié)點的前一個節(jié)點,才能完成刪除——時間復(fù)雜度為 O(N)

  • 如果是 C++,一定要注意 delete 指針指向的內(nèi)存后,必須將指針重新指向 nullptr

    delete p; p = nullptr;
  • 總的時間復(fù)雜度:[(n-1)O(1) + O(n)] / n = O(1)

Code

void DeleteNode(ListNode** pListHead, ListNode* pToBeDeleted) {if(!pListHead || !pToBeDeleted)return;if(pToBeDeleted->next != nullptr) { // 要刪除的結(jié)點不是尾結(jié)點ListNode* p = pToBeDeleted->next;pToBeDeleted->val = p->val;pToBeDeleted->next = p->next;delete p; // delete 指針指向的內(nèi)存后,必須將指針重新指向 nullptrp = nullptr; }else if(*pListHead == pToBeDeleted) { // 鏈表只有一個結(jié)點,刪除頭結(jié)點delete pToBeDeleted;pToBeDeleted = nullptr; *pListHead = nullptr;}else { // 鏈表中有多個結(jié)點,刪除尾結(jié)點ListNode* p = *pListHead;while(p->next != pToBeDeleted)p = p->next; p->next = nullptr;delete pToBeDeleted;pToBeDeleted = nullptr;} }

18.2 刪除鏈表中重復(fù)的結(jié)點(鏈表)

刪除鏈表中重復(fù)的結(jié)點 - NowCoder

題目描述

在一個排序的鏈表中,存在重復(fù)的結(jié)點,請刪除該鏈表中重復(fù)的結(jié)點; 重復(fù)的結(jié)點不保留,返回鏈表頭指針。 例如,鏈表1->2->3->3->4->4->5 處理后為 1->2->5

思路

  • 注意重復(fù)的節(jié)點不保留,所以要特別注意頭結(jié)點也重復(fù)的情況——最好的做法是新設(shè)一個頭結(jié)點
  • delete 指針指向的內(nèi)存后,必須將指針重新指向 nullptr

Code

class Solution { public:ListNode * deleteDuplication(ListNode* pHead){ if (pHead == NULL) return pHead;ListNode* head = new ListNode{-1}; // 設(shè)置一個頭結(jié)點head->next = pHead;ListNode* pre = head;ListNode* cur = pHead;while (cur != NULL && cur->next != NULL) {if (cur->val != cur->next->val) { // 不重復(fù)時向后遍歷pre = cur;cur = cur->next;}else { // 發(fā)現(xiàn)重復(fù)int val = cur->val;while (cur != NULL && cur->val == val) { // 循環(huán)刪除重復(fù)auto tmp = cur;cur = cur->next;delete tmp; // delete + nullptrtmp = nullptr;}pre->next = cur;}}auto ret = head->next;delete head; // delete + nullptrhead = nullptr;return ret;} };

19. 正則表達式匹配(自動機:動態(tài)規(guī)劃 | DFS)

正則表達式匹配 - NowCoder

題目描述

請實現(xiàn)一個函數(shù)用來匹配包括'.'和'*'的正則表達式。 模式中的字符'.'表示任意一個字符,而'*'表示它前面的字符可以出現(xiàn)任意次(包含0次)。 在本題中,匹配是指字符串的所有字符匹配整個模式。 例如,字符串"aaa"與模式"a.a"和"ab*ac*a"匹配,但是與"aa.a"和"ab*a"均不匹配

思路

  • ‘.’ 用于當(dāng)做一個任意字符,’*’ 用于重復(fù)前面的字符,注意兩者區(qū)別
  • 下面提供 dfs(C++) 和 dp(Java) 兩種做法

Code - dfs

class Solution { public:bool match(char* str, char* pattern) {if(str==NULL||pattern==NULL)return false;return dfs(str,pattern);}bool dfs(char* str, char* pattern) {if(*str=='\0'&&*pattern=='\0')return true;if(*str!='\0'&&*pattern=='\0')return false;if(*(pattern+1)=='*') {if(*pattern==*str||(*pattern=='.'&&*str!='\0'))/*dfs(str,pattern+2): 模式串不匹配dfs(str+1,pattern): 模式串已經(jīng)匹配成功,嘗試匹配下一個字符串dfs(str+1,pat+2): 模式串已經(jīng)成功匹配,并且不匹配下一個字符串內(nèi)容 */return dfs(str+1,pattern)||dfs(str,pattern+2);elsereturn dfs(str,pattern+2);}if(*str==*pattern||(*pattern=='.'&&*str!='\0'))return dfs(str+1,pattern+1);return false;} };

Code - dp

public boolean match(char[] str, char[] pattern) {int m = str.length, n = pattern.length;boolean[][] dp = new boolean[m + 1][n + 1];dp[0][0] = true;for (int i = 1; i <= n; i++)if (pattern[i - 1] == '*')dp[0][i] = dp[0][i - 2];for (int i = 1; i <= m; i++)for (int j = 1; j <= n; j++)if (str[i - 1] == pattern[j - 1] || pattern[j - 1] == '.')dp[i][j] = dp[i - 1][j - 1];else if (pattern[j - 1] == '*')if (pattern[j - 2] == str[i - 1] || pattern[j - 2] == '.') {dp[i][j] |= dp[i][j - 1]; // a* counts as single adp[i][j] |= dp[i - 1][j]; // a* counts as multiple adp[i][j] |= dp[i][j - 2]; // a* counts as empty} elsedp[i][j] = dp[i][j - 2]; // a* only counts as emptyreturn dp[m][n]; }

20. 表示數(shù)值的字符串(自動機 | 正則)

表示數(shù)值的字符串 - NowCoder

題目描述

請實現(xiàn)一個函數(shù)用來判斷字符串是否表示數(shù)值(包括整數(shù)和小數(shù))。 例如,字符串"+100","5e2","-123","3.1416"和"-1E-16"都表示數(shù)值。 但是"12e","1a3.14","1.2.3","+-5"和"12e+4.3"都不是。

思路

  • if 判斷 - 自動機
  • 正則表達式

Code - 自動機

class Solution { public:// 數(shù)字的格式可以用A[.[B]][e|EC]或者.B[e|EC]表示,其中A和C都是// 整數(shù)(可以有正負號,也可以沒有),而B是一個無符號整數(shù)bool isNumeric(const char* str) {if(str == nullptr)return false;bool numeric = scanInteger(&str);// 如果出現(xiàn)'.',接下來是數(shù)字的小數(shù)部分if(*str == '.') {++str;// 下面一行代碼用||的原因:// 1. 小數(shù)可以沒有整數(shù)部分,例如.123等于0.123;// 2. 小數(shù)點后面可以沒有數(shù)字,例如233.等于233.0;// 3. 當(dāng)然小數(shù)點前面和后面可以有數(shù)字,例如233.666numeric = scanUnsignedInteger(&str) || numeric;}// 如果出現(xiàn)'e'或者'E',接下來跟著的是數(shù)字的指數(shù)部分if(*str == 'e' || *str == 'E') {++str;// 下面一行代碼用&&的原因:// 1. 當(dāng)e或E前面沒有數(shù)字時,整個字符串不能表示數(shù)字,例如.e1、e1;// 2. 當(dāng)e或E后面沒有整數(shù)時,整個字符串不能表示數(shù)字,例如12e、12e+5.4numeric = numeric && scanInteger(&str);}return numeric && *str == '\0';}bool scanUnsignedInteger(const char** str) {const char* before = *str;while(**str != '\0' && **str >= '0' && **str <= '9')++(*str);// 當(dāng)str中存在若干0-9的數(shù)字時,返回truereturn *str > before;}// 整數(shù)的格式可以用[+|-]B表示, 其中B為無符號整數(shù)bool scanInteger(const char** str) {if(**str == '+' || **str == '-')++(*str);return scanUnsignedInteger(str);} };

Code - 正則(Python)

import re class Solution:# s字符串def isNumeric(self, s):# Python 中完全匹配需要以 ^ 開頭,以 $ 結(jié)尾# r"" 表示不轉(zhuǎn)義# if re.match("^[+-]?\\d*(\\.\\d+)?([eE][+-]?\\d+)?$", s):if re.match(r"^[+-]?\d*(\.\d+)?([eE][+-]?\d+)?$", s):return Trueelse:return False

Code - 正則(C++)

#include <regex>class Solution { public:bool isNumeric(char* string) {regex reg("[+-]?\\d*(\\.\\d+)?([eE][+-]?\\d+)?");return regex_match(string, reg);} };

Code - 正則(Java)

public class Solution {public boolean isNumeric(char[] str) {if (str == null)return false;return new String(str).matches("[+-]?\\d*(\\.\\d+)?([eE][+-]?\\d+)?");} }

21. 調(diào)整數(shù)組順序使奇數(shù)位于偶數(shù)前面(數(shù)組)

調(diào)整數(shù)組順序使奇數(shù)位于偶數(shù)前面 - NowCoder

題目描述

輸入一個整數(shù)數(shù)組,實現(xiàn)一個函數(shù)來調(diào)整該數(shù)組中數(shù)字的順序, 使得所有的奇數(shù)位于數(shù)組的前半部分,所有的偶數(shù)位于數(shù)組的后半部分, 并保證奇數(shù)和奇數(shù),偶數(shù)和偶數(shù)之間的相對位置不變。
  • 要求:空間復(fù)雜度 O(1)
  • 本題與原書不同,這里要求相對順序不變,原書的側(cè)重點在于函數(shù)指針

思路

  • 如果可以使用額外空間,那么問題就很簡單
  • 如果不想使用額外空間,那么只能通過循環(huán)移位來達到避免覆蓋的目的,時間復(fù)雜度 O(N^2)
    • 可以利用“冒泡排序”的思想避免循環(huán)位移

Code - 使用額外空間

class Solution { public:void reOrderArray(vector<int> &array) {vector<int> odd; // 存奇數(shù)vector<int> eve; // 存偶數(shù)for (auto i : array) {if (i & 1) // 是奇數(shù)odd.push_back(i);elseeve.push_back(i);}array.swap(odd);array.insert(array.end(), eve.begin(), eve.end());} };

Code - 不使用額外空間

討論區(qū)第二個回答

class Solution { public:void reOrderArray(vector<int> &array) {for(int i = 0; i < array.size() / 2; i++)for(int j = 0; j < array.size()-i; j++)if((array[j]%2 == 0) && (array[j+1]%2 == 1))swap(array[j] ,array[j+1]);} };

22. 鏈表中倒數(shù)第 K 個結(jié)點(鏈表 + 雙指針)

鏈表中倒數(shù)第k個結(jié)點 - NowCoder

題目描述

輸入一個鏈表,輸出該鏈表中倒數(shù)第k個結(jié)點。

思路

  • 設(shè)置快慢指針快指針先走 k-1 步,然后慢指針開始走,當(dāng)快指針到達鏈表尾時,慢指針即指向倒數(shù)第 k 個節(jié)點
  • 健壯性檢驗:
    • 輸入是一個空鏈表
    • 鏈表長度小于 k

Code

class Solution { public:ListNode * FindKthToTail(ListNode* pListHead, unsigned int k) {if(pListHead == nullptr)return nullptr;ListNode * slow = pListHead;ListNode * fast = pListHead;//先讓 fast 走 k-1 步while (k && fast) {fast = fast->next;k--;}// 如果 k > 0,說明 k 大于鏈表長度if (k > 0)return nullptr;// 接著讓兩個指針一起往后走,當(dāng) fast 到最后時,slow 即指向倒數(shù)第 k 個while (fast) {fast = fast->next;slow = slow->next;}return slow;} };

23. 鏈表中環(huán)的入口結(jié)點(鏈表 + 雙指針)

鏈表中環(huán)的入口結(jié)點 - NowCoder

題目描述

給一個鏈表,若其中包含環(huán),請找出該鏈表的環(huán)的入口結(jié)點,否則,輸出null。
  • 要求:不使用額外空間

思路

  • 快慢雙指針

  • 快指針 fast 每次移動 2 步,慢指針 slow 每次 1 步;因為存在環(huán),fast 和 slow 總會相遇,此時 fast 剛好比 slow 多走一圈(?)

  • 如圖,假設(shè)他們相遇在 z1 點,此時將 fast/slow 之一重新指向頭結(jié)點,繼續(xù)每次一步移動,它們再次相遇的點就是入口

Code

class Solution { public:ListNode * EntryNodeOfLoop(ListNode* pHead) {if (pHead == NULL) return nullptr;ListNode* slow = pHead;ListNode* fast = pHead;while (fast != NULL && fast->next != NULL) {slow = slow->next;fast = fast->next->next;if (slow == fast) { // 找到環(huán)中相遇點slow = pHead; // 將 fast/slow 中的任一個重新指向頭指針while (slow != fast) { // 直到他們再次相遇,相遇的這個點就是入口slow = slow->next;fast = fast->next;}return slow;}}return nullptr;} };

24. 反轉(zhuǎn)鏈表(鏈表)

反轉(zhuǎn)鏈表 - NowCoder

題目描述

輸入一個鏈表,反轉(zhuǎn)鏈表后,輸出新鏈表的表頭。
  • 要求:不使用額外空間

思路

  • 可以輔助圖示思考

Code - 迭代

class Solution { public:ListNode * ReverseList(ListNode* pHead) {if (pHead == NULL)return NULL;ListNode* cur = pHead;ListNode* pre = NULL;ListNode* nxt = cur->next;cur->next = NULL; // 斷開當(dāng)前節(jié)點及下一個節(jié)點while (nxt) {pre = cur;cur = nxt;nxt = nxt->next;cur->next = pre;}return cur;} };

Code - 遞歸

class Solution { public:ListNode * ReverseList(ListNode* pHead) {if (pHead == nullptr || pHead->next == nullptr)return pHead;auto nxt = pHead->next;pHead->next = nullptr; // 斷開當(dāng)前節(jié)點及下一個節(jié)點auto newHead = ReverseList(nxt);nxt->next = pHead;return newHead;} };

25. 合并兩個排序的鏈表(鏈表)

合并兩個排序的鏈表 - NowCoder

題目描述

輸入兩個單調(diào)遞增的鏈表,輸出兩個鏈表合成后的鏈表,當(dāng)然我們需要合成后的鏈表滿足單調(diào)不減規(guī)則。

Code - 迭代

class Solution { public:ListNode * Merge(ListNode* pHead1, ListNode* pHead2) {ListNode head{-1};ListNode *cur = &head;while (pHead1 && pHead2) {if (pHead1->val <= pHead2->val) {cur->next = pHead1;pHead1 = pHead1->next;} else {cur->next = pHead2;pHead2 = pHead2->next;}cur = cur->next;}if (pHead1) cur->next = pHead1;if (pHead2) cur->next = pHead2;return head.next;} };

Code - 遞歸

class Solution { public:ListNode* Merge(ListNode* pHead1, ListNode* pHead2) {if (!pHead1) return pHead2;if (!pHead2) return pHead1;if(pHead1->val <= pHead2->val){pHead1->next = Merge(pHead1->next, pHead2);return pHead1;} else {pHead2->next = Merge(pHead1, pHead2->next);return pHead2;}} };

26. 樹的子結(jié)構(gòu)(二叉樹)

樹的子結(jié)構(gòu) -NowCoder

題目描述

輸入兩棵二叉樹A,B,判斷B是不是A的子結(jié)構(gòu)。 約定空樹不是任意一個樹的子結(jié)構(gòu)。
  • 圖示

思路

  • 遞歸
  • 有兩個遞歸的點:一、遞歸尋找與子樹根節(jié)點相同的點;二、遞歸判斷子結(jié)構(gòu)是否相同

Code

class Solution { public:bool HasSubtree(TreeNode* pRoot1, TreeNode* pRoot2) {if (pRoot2 == NULL || pRoot1 == NULL)return false;// 遞歸尋找與子樹根節(jié)點相同的點return isSubTree(pRoot1, pRoot2)|| HasSubtree(pRoot1->left, pRoot2)|| HasSubtree(pRoot1->right, pRoot2);}bool isSubTree(TreeNode* pRoot1, TreeNode* pRoot2) {if (pRoot2 == NULL) return true;if (pRoot1 == NULL) return false;// 遞歸判斷子結(jié)構(gòu)是否相同if (pRoot1->val == pRoot2->val)return isSubTree(pRoot1->left, pRoot2->left)&& isSubTree(pRoot1->right, pRoot2->right);elsereturn false;} };

27. 二叉樹的鏡像(二叉樹)

二叉樹的鏡像 - NowCoder

題目描述

操作給定的二叉樹,將其變換為源二叉樹的鏡像。
  • 圖示

思路

  • 前序遍歷,每次交換節(jié)點的左右子樹;即必須先交換節(jié)點的左右子樹后,才能繼續(xù)遍歷

Code

class Solution { public:void Mirror(TreeNode *pRoot) {if (pRoot == nullptr) return;auto tmp = pRoot->left;pRoot->left = pRoot->right;pRoot->right = tmp;Mirror(pRoot->left);Mirror(pRoot->right);} };

28 對稱的二叉樹(二叉樹)

對稱的二叉樹 NowCoder

題目描述

請實現(xiàn)一個函數(shù),用來判斷一顆二叉樹是不是對稱的。 注意,如果一個二叉樹同此二叉樹的鏡像是同樣的,定義其為對稱的。 空樹也認為是對稱的

思路

  • 遞歸
  • 同時遍歷左子樹和右子樹,然后是“左子樹的左子樹和右子樹的右子樹”,及左子樹的右子樹和右子樹的左子樹,遞歸以上步驟

Code

class Solution { public:bool isSymmetrical(TreeNode* pRoot) {if (pRoot == nullptr) return true;return dfs(pRoot->left, pRoot->right);}bool dfs(TreeNode* l, TreeNode* r) {if (l == nullptr && r == nullptr)return true;if (l == nullptr || r == nullptr) // 注意這個條件return false;if (l->val == r->val)return dfs(l->left, r->right)&& dfs(l->right, r->left);elsereturn false;} };

29. 順時針打印矩陣(二維數(shù)組)

順時針打印矩陣 - NowCoder

題目描述

下圖的矩陣順時針打印結(jié)果為:1, 2, 3, 4, 8, 12, 16, 15, 14, 13, 9, 5, 6, 7, 11, 10
  • 圖示

  • 注意,不是蛇形打印,而是一層一層順時針打印

思路

  • 二維數(shù)組遍歷

Code

class Solution { public:vector<int> printMatrix(vector<vector<int> > matrix) {vector<int> ret;int rl = 0, rr = matrix.size()-1;int cl = 0, cr = matrix[0].size()-1;while(rl <= rr && cl <= cr) {for (int i = cl; i <= cr; i++)ret.push_back(matrix[rl][i]);for (int i = rl+1; i <= rr; i++)ret.push_back(matrix[i][cr]);if (rl != rr)for (int i = cr - 1; i >= cl; i--)ret.push_back(matrix[rr][i]);if (cl != cr)for (int i = rr - 1; i > rl; i--)ret.push_back(matrix[i][cl]);rl++; rr--; cl++; cr--;}return ret;} };

30. 包含 min 函數(shù)的棧(數(shù)據(jù)結(jié)構(gòu):棧)

包含min函數(shù)的棧 - NowCoder

題目描述

定義棧的數(shù)據(jù)結(jié)構(gòu),請在該類型中實現(xiàn)一個能夠得到棧中所含最小元素的min函數(shù)
  • 要求:時間復(fù)雜度 O(1)

思路

  • 因為要求在常數(shù)時間內(nèi)完成所有操作,所以不能有排序操作
  • 使用一個輔助棧保存最小、次小、…

Code

class Solution {stack<int> s;stack<int> s_min;public:void push(int value) {s.push(value);if (s_min.empty())s_min.push(value);if (value <= s_min.top()) // 注意是小于等于s_min.push(value);}void pop() {if (s.top() == s_min.top())s_min.pop();s.pop();}int top() {return s.top();}int min() {return s_min.top();}};

31. 棧的壓入、彈出序列(數(shù)據(jù)結(jié)構(gòu):棧)

棧的壓入、彈出序列 -NowCoder

題目描述

輸入兩個整數(shù)序列,第一個序列表示棧的壓入順序,請判斷第二個序列是否為該棧的彈出順序。 假設(shè)壓入棧的所有數(shù)字均不相等。 例如序列 1,2,3,4,5 是某棧的壓入順序,序列 4,5,3,2,1 是該壓棧序列對應(yīng)的一個彈出序列, 但 4,3,5,1,2 就不可能是該壓棧序列的彈出序列。

思路

  • 使用一個輔助棧
  • 依次將入棧序列入棧,如果棧頂元素等于出棧序列的棧頂元素,則彈出
  • 當(dāng)流程無法繼續(xù)時,如果輔助棧是空的,則出棧序列是符合的

Code

class Solution { public:bool IsPopOrder(vector<int> pushV, vector<int> popV) {if (pushV.empty()) return false;stack<int> tmp;int j = 0;for (int i = 0; i < pushV.size(); i++) {tmp.push(pushV[i]);while (!tmp.empty() && tmp.top() == popV[j]) {tmp.pop();j++;}}return tmp.empty();} };

32.1 從上往下打印二叉樹(BFS)

從上往下打印二叉樹 - NowCoder

題目描述

從上往下打印出二叉樹的每個節(jié)點,同層節(jié)點從左至右打印。 例如,以下二叉樹層次遍歷的結(jié)果為:1,2,3,4,5,6,7
  • 圖示

思路

  • 廣度優(yōu)先搜索 + 隊列
  • 注意入隊時先左子節(jié)點,后右節(jié)點
  • 注意不需要修改原二叉樹

Code

class Solution {queue<TreeNode*> q; // 輔助隊列 public:vector<int> PrintFromTopToBottom(TreeNode* root) {if (root == nullptr) return vector<int>();q.push(root);vector<int> ret;while (!q.empty()) {auto cur = q.front();q.pop();ret.push_back(cur->val);if (cur->left != nullptr)q.push(cur->left);if (cur->right != nullptr)q.push(cur->right);}return ret;} };

32.2 分行從上到下打印二叉樹(BFS)

把二叉樹打印成多行 - NowCoder

題目描述

從上到下按層打印二叉樹,同一層結(jié)點從左至右輸出。每一層輸出一行。

思路

  • 除了利用隊列和 BFS
  • 為了分行輸出,還需要兩個變量:一個表示在當(dāng)前層中還沒有打印的節(jié)點數(shù)、一個表示下一層的節(jié)點數(shù)
  • 注意根節(jié)點為空的情況

Code

class Solution {queue<TreeNode*> q; public:vector<vector<int>> Print(TreeNode* pRoot) {if (pRoot == nullptr) return vector<vector<int>>();q.push(pRoot);int curL = 1; // 當(dāng)前層的節(jié)點數(shù),初始化為 1,根節(jié)點int nxtL = 0; // 下一層的節(jié)點數(shù)vector<vector<int>> ret;vector<int> tmp;while (!q.empty()) {auto node = q.front();q.pop();curL--;tmp.push_back(node->val);if (node->left != nullptr) {q.push(node->left);nxtL++;}if (node->right != nullptr) {q.push(node->right);nxtL++;}if (curL == 0) {ret.push_back(tmp);tmp.clear();curL = nxtL;nxtL = 0;}}return ret;} };

32.3 按之字形順序打印二叉樹(BFS)

按之字形順序打印二叉樹 - NowCoder

題目描述

請實現(xiàn)一個函數(shù)按照之字形打印二叉樹, 即第一行按照從左到右的順序打印,第二層按照從右至左的順序打印, 第三行按照從左到右的順序打印,其他行以此類推。

思路

  • 利用一個隊列+一個棧,分奇偶討論;
  • 使用兩個棧,根據(jù)奇偶,改變左右子樹的入棧/入隊順序
  • 利用雙端隊列,分奇偶改變?nèi)腙?出隊方向(C++ 不推薦,編碼量大)(有坑,不好寫)
  • 反轉(zhuǎn)層結(jié)果:根據(jù)奇偶,判斷是否反轉(zhuǎn)中間結(jié)果(最直觀的方法)
  • Code - 兩個棧

    class Solution { public:vector<vector<int>> Print(TreeNode* pRoot) {if(pRoot == nullptr)return vector<vector<int>>();// 定義兩個棧,s[0] 始終存放偶數(shù)層的節(jié)點,s[1] 始終存放奇數(shù)層的節(jié)點stack<TreeNode*> s[2];int cur = 0; // 當(dāng)前層(假設(shè)根節(jié)點所在的第0層是偶數(shù)層)s[cur & 1].push(pRoot); // 第 0 層入棧vector<vector<int>> ret;vector<int> tmp;while(!s[0].empty() || !s[1].empty()) {auto pNode = s[cur & 1].top();s[cur & 1].pop();tmp.push_back(pNode->val);if(cur & 1) { // 當(dāng)前是奇數(shù)層// 下一層是偶數(shù)層// 先壓右節(jié)點if(pNode->right != nullptr)s[0].push(pNode->right);if(pNode->left != nullptr)s[0].push(pNode->left);}else {// 下一層是奇數(shù)層,壓入 s1// 先壓左節(jié)點if(pNode->left != nullptr)s[1].push(pNode->left);if(pNode->right != nullptr)s[1].push(pNode->right);}if(s[cur & 1].empty()) {ret.push_back(tmp);tmp.clear();cur++; // 累計層數(shù)}}return ret;} };

    Code - 層反轉(zhuǎn)

    class Solution {queue<TreeNode*> q; public:vector<vector<int>> Print(TreeNode* pRoot) {if (pRoot == nullptr) return vector<vector<int>>();q.push(pRoot);int cur = 0; // 當(dāng)前層int curL = 1; // 當(dāng)前層的節(jié)點數(shù),初始化為 1,根節(jié)點int nxtL = 0; // 下一層的節(jié)點數(shù)vector<vector<int>> ret;vector<int> tmp;while (!q.empty()) {auto node = q.front();q.pop();curL--;tmp.push_back(node->val);if (node->left != nullptr) {q.push(node->left);nxtL++;}if (node->right != nullptr) {q.push(node->right);nxtL++;}if (curL == 0) {if (cur & 1) // 如果是奇數(shù)層,就反轉(zhuǎn)中間結(jié)果reverse(tmp.begin(), tmp.end());cur++;ret.push_back(tmp);tmp.clear();curL = nxtL;nxtL = 0;}}return ret;} };

    33. 二叉搜索樹的后序遍歷序列(二叉樹:遞歸)

    二叉搜索樹的后序遍歷序列 - NowCoder

    題目描述

    輸入一個整數(shù)數(shù)組,判斷該數(shù)組是不是某二叉搜索樹的后序遍歷的結(jié)果。 如果是則輸出Yes,否則輸出No。假設(shè)輸入的數(shù)組的任意兩個數(shù)字都互不相同。

    思路

    • 二叉搜索樹:左子樹都小于根節(jié)點,右子樹都大于根節(jié)點,遞歸定義
    • 后序遍歷:會先輸出整個左子樹,再輸出右子樹,最后根節(jié)點;也就是說,數(shù)組可以被劃分為三個部分
      • 示例:1,2,3 | 5,6,7 | 4 第一部分都小于最后的元素,第二部分都大于最后的元素——雖然這不是一顆二叉搜索樹,但是它滿足第一次判斷的結(jié)果,后序再遞歸判斷左右子樹

    Code

    class Solution { public:bool VerifySquenceOfBST(vector<int> s) {if (s.empty()) return false;return dfs(s, 0, s.size()-1);}bool dfs(vector<int> &s, int l, int r) {if (l >= r) return true;int base = s[r]; // 根節(jié)點int mid = 0; // 尋找第一個大于根節(jié)點的元素for (; mid < r; mid++)if (s[mid] > base)break;bool flag = true; // 如果第一個大于根節(jié)點的元素到根節(jié)點之間的元素都大于根節(jié)點for (int i = mid; i<r; i++)if (s[i] < base) {flag = false;break;}return flag && dfs(s, l, mid-1) && dfs(s, mid, r-1); // 遞歸判斷} };

    34. 二叉樹中和為某一值的路徑(DFS)

    二叉樹中和為某一值的路徑 - NowCoder

    題目描述

    輸入一顆二叉樹的跟節(jié)點和一個整數(shù),打印出二叉樹中結(jié)點值的和為輸入整數(shù)的所有路徑。 路徑定義為從樹的根結(jié)點開始往下一直到葉結(jié)點所經(jīng)過的結(jié)點形成一條路徑。 (注意: 在返回值的list中,數(shù)組長度大的數(shù)組靠前)

    思路

    • 注意,必須要從根節(jié)點到葉子節(jié)點,才叫一條路徑,中間結(jié)果都不算路徑

    Code

    class Solution { public:vector<vector<int>> ret;vector<int> trace;vector<vector<int> > FindPath(TreeNode* root, int expectNumber) {if (root != nullptr)dfs(root, expectNumber);return ret;}void dfs(TreeNode* cur, int n) {trace.push_back(cur->val);// 結(jié)束條件if (cur->left == nullptr && cur->right == nullptr) {if (cur->val == n)ret.push_back(trace); // C++ 默認深拷貝}if (cur->left)dfs(cur->left, n - cur->val); // 這里沒有求和,而是用遞減的方式if (cur->right)dfs(cur->right, n - cur->val);trace.pop_back();} };

    35. 復(fù)雜鏈表的復(fù)制(鏈表)

    復(fù)雜鏈表的復(fù)制 - NowCoder

    題目描述

    輸入一個復(fù)雜鏈表—— 每個節(jié)點中有節(jié)點值,以及兩個指針,一個指向下一個節(jié)點,另一個特殊指針指向任意一個節(jié)點, 返回結(jié)果為復(fù)制后鏈表的頭節(jié)點。 (注意,輸出結(jié)果中請不要返回參數(shù)中的節(jié)點引用,否則判題程序會直接返回空)
    • 要求:時間復(fù)雜度 O(N)

    思路

    • 基本思路 O(N^2)
      • 第一步,依次復(fù)制每個節(jié)點
      • 第二步,對每個節(jié)點,尋找特殊指針指向的節(jié)點;因為特殊指針的位置不定,必須從頭開始找
        • 假設(shè)經(jīng)過 m 步找到了某個節(jié)點的特殊節(jié)點,那么在新鏈表中也走 m 步
    • 問題的難點在于不知道特殊指針?biāo)傅墓?jié)點在新鏈表中位置
    • 一個經(jīng)典的方法
      • 第一步,復(fù)制每個節(jié)點,如:原來是 A->B->C 變成 A->A'->B->B'->C->C';
      • 第二步,遍歷鏈表,使:A'->random = A->random->next;
      • 第三步,拆分鏈表

    Code

    class Solution { public:RandomListNode * Clone(RandomListNode* pHead) {if (!pHead) return NULL;RandomListNode *cur = pHead;// 1. 復(fù)制每個節(jié)點,如:原來是A->B->C 變成A->A'->B->B'->C->C'while (cur) {RandomListNode* node = new RandomListNode(cur->label);node->next = cur->next; // 注意順序cur->next = node;cur = node->next;}// 2. 遍歷鏈表,使:A'->random = A->random->next;cur = pHead;RandomListNode* tmp;while (cur) {tmp = cur->next;if (cur->random != nullptr) {tmp->random = cur->random->next;}cur = cur->next->next; // 跳過復(fù)制的節(jié)點}// 3. 拆分鏈表cur = pHead;RandomListNode* ret = cur->next;while (cur->next) {tmp = cur->next;cur->next = tmp->next;cur = tmp;}return ret;} };

    36. 二叉搜索樹與雙向鏈表(DFS)

    二叉搜索樹與雙向鏈表 - NowCoder

    題目描述

    輸入一棵二叉搜索樹,將該二叉搜索樹轉(zhuǎn)換成一個排序的雙向鏈表。 要求不能創(chuàng)建任何新的結(jié)點,只能調(diào)整樹中結(jié)點指針的指向。

    思路

    • 因為要求是有序鏈表,因此考慮中序遍歷
    • 利用兩個額外的指針保存前一個節(jié)點和頭結(jié)點,具體見代碼中注釋

    Code

    class Solution { public:TreeNode * pre; // 記錄上一個節(jié)點TreeNode * ret; // 雙向鏈表的頭結(jié)點TreeNode * Convert(TreeNode* pRootOfTree) {// C++ 小坑,不能在類類初始化,默認初始化不為 NULLpre = nullptr;ret = nullptr;dfs(pRootOfTree);return ret;}// 中序遍歷void dfs(TreeNode* node) {if (node == nullptr) return;dfs(node->left);if (ret == nullptr) // 到達最左葉子,即鏈表頭;只會執(zhí)行一次ret = node;// 第一次執(zhí)行該語句時,pre == nullptr;這并不矛盾。// 因為頭節(jié)點的前一個指針就是指向 nullptr 的node->left = pre;if (pre != nullptr)pre->right = node;pre = node;dfs(node->right);} };

    37. 序列化二叉樹(DFS)***

    序列化二叉樹 - NowCoder

    題目描述

    請實現(xiàn)兩個函數(shù),分別用來序列化和反序列化二叉樹。 接口如下:char* Serialize(TreeNode *root);TreeNode* Deserialize(char *str);
    • 比如中序遍歷就是一個二叉樹序列化
    • 反序列化要求能夠通過序列化的結(jié)果還原二叉樹
    • 空節(jié)點用 ‘#’ 表示,節(jié)點之間用空格分開

    思路

    • 一般在做樹的遍歷時,會以非空葉子節(jié)點作為最底層,此時還原二叉樹必須要前序遍歷+中序遍歷或后序遍歷
    • 如果以空節(jié)點作為樹的最底層,那么只需要前序遍歷就能還原二叉樹,而且能與反序列化同步進行(這是最關(guān)鍵的一點)

    Code

    class Solution {// 因為接口限制,所以需要使用了兩個 ssstringstream ss;stringstream sd;char ret[1024];//char* ret;void dfs_s(TreeNode *node) {if (node == nullptr) {ss << "#";return;}ss << node->val;ss << " ";dfs_s(node->left);ss << " ";dfs_s(node->right);}TreeNode* dfs_d() {if (sd.eof())return nullptr;string val; // 只能用 string 接收,用 int 或 char 都會有問題sd >> val;if (val == "#")return nullptr;TreeNode* node = new TreeNode{ stoi(val) }; // node->left = dfs_d();node->right = dfs_d();return node;}public:char* Serialize(TreeNode *root) {dfs_s(root);// 這里耗了很久// return (char*)ss.str().c_str(); // 會出問題,原因未知return strcpy(ret, ss.str().c_str());}TreeNode* Deserialize(char *str) {if (strlen(str) < 1) return nullptr;sd << str;return dfs_d();} };

    38. 字符串的排列(DFS)

    字符串的排列 - NowCoder

    排列組合專題 TODO

    題目描述

    輸入一個字符串,按字典序打印出該字符串中字符的所有排列。 例如輸入字符串 abc, 則打印出由字符 a,b,c 所能排列出來的所有字符串 abc, acb, bac, bca, cab 和 cba。

    思路

    • 深度優(yōu)先搜索

    Code

    class Solution {string s;string tmp;int strlen;vector<string> ret;vector<int> used;void dfs(int step) {if (step == strlen) {ret.push_back(tmp);return;}for (int i = 0; i<strlen; i++) {if (used[i]) continue;if (i > 0 && s[i] == s[i-1] && !used[i-1])continue;tmp[step] = s[i];used[i] = 1;dfs(step + 1);used[i] = 0;}}public:vector<string> Permutation(string str) {if (str.empty()) return vector<string>();// 當(dāng)做全局變量s = str;strlen = s.length();sort(s.begin(), s.end()); // 因為可能存在重復(fù),所以需要先排序,將重復(fù)的字符集合在一起// 初始化tmp.resize(strlen, '\0');used.resize(strlen, 0);dfs(0);return ret;} };

    39.1 數(shù)組中出現(xiàn)次數(shù)超過一半的數(shù)字(多數(shù)投票問題)

    數(shù)組中出現(xiàn)次數(shù)超過一半的數(shù)字 - NowCoder

    題目描述

    數(shù)組中有一個數(shù)字出現(xiàn)的次數(shù)超過數(shù)組長度的一半,請找出這個數(shù)字。 例如輸入一個長度為9的數(shù)組{1,2,3,2,2,2,5,4,2}。 由于數(shù)字2在數(shù)組中出現(xiàn)了5次,超過數(shù)組長度的一半,因此輸出2。 如果不存在則輸出0。
    • 要求:時間復(fù)雜度 O(N),空間復(fù)雜度 O(1)

    思路

  • 多數(shù)投票問題(Majority Vote Algorithm)
    • 設(shè)置一個計數(shù)器 cnt 和保存最多元素的變量 majority
    • 如果 cnt==0,則將 majority 設(shè)為當(dāng)前元素
    • 如果 majority 和當(dāng)前元素值相同,則 cnt++,反之 cnt--
    • 重復(fù)以上兩步,直到掃描完數(shù)組
    • cnt 賦值為 0,再次掃描數(shù)組,如果數(shù)組元素與 majority 相同,cnt++
    • 如果掃描結(jié)束后,cnt > nums.size() / 2,則返回 majority,否則返回 0。
  • 找出數(shù)組中第 k 大的數(shù)字
  • Code

    class Solution {int cnt;int majority; public:int MoreThanHalfNum_Solution(vector<int> nums) {if (nums.empty()) return 0;cnt = 0;for (int i=0; i<nums.size(); i++) {if (cnt == 0)majority = nums[i];if (nums[i] == majority)cnt++;elsecnt--;}cnt = 0;for (auto i : nums) {if (i == majority)cnt++;}return cnt > nums.size()/2 ? majority : 0;} };

    40. 找出數(shù)組中第 k 大的數(shù)字(數(shù)據(jù)結(jié)構(gòu):堆)***

    數(shù)組中的第K個最大元素 - LeetCode

    最小的K個數(shù) - NowCoder

    海量數(shù)據(jù) Top K 專題 TODO

    題目描述

    找出數(shù)組中第 k 大的數(shù)/前 k 大的數(shù)/第 k 個最大的數(shù)
    • 正序找第 k 大的元素和逆序找第 k 大的元素方法是一致的;牛客是前者,LeetCode 是后者
    • 可以改變原數(shù)組
      • 要求:時間復(fù)雜度 O(N),空間復(fù)雜度 O(1)
    • 不可以改變原數(shù)組
      • 要求:時間復(fù)雜度 O(NlogK),空間復(fù)雜度 O(K)
    • 實際上,找出數(shù)組中出現(xiàn)次數(shù)超過一半的數(shù)字可以看做是找出數(shù)組中第 n/2 大的數(shù)字

    思路

    • 可以改變原數(shù)組時:
      • 參考快速排序中的 partition([pɑ:?t??n]) 過程
      • 經(jīng)過一次 partition 后,數(shù)組被 pivot 分成左右兩部分:l 和 r。
        • 當(dāng) |l| = k-1 時,pivot 即是所找的第 k 大的數(shù);
        • 當(dāng) |l| < k-1,所找的數(shù)位于 r 中;
        • 當(dāng) |l| > k-1,所找的數(shù)位于 l 中.
    • 不可以改變原數(shù)組
      • 使用額外空間 - 優(yōu)先隊列(堆)或 multiset

    Code - 優(yōu)先隊列(無優(yōu)化 O(NlogN))(牛客)

    class Solution {vector<int> ret; public:vector<int> GetLeastNumbers_Solution(vector<int>& nums, int k) {// 注意越界條件if (nums.empty() || k <= 0 || k > nums.size()) return vector<int>();if (k == nums.size())return vector<int>(nums);// 構(gòu)造最小堆,注意:priority_queue 默認是最小堆 priority_queue<int, vector<int>, greater<int>> p;for (auto i : nums) // 缺點,需要完全放入數(shù)組,如果是從 100000 個中找前 2 個p.push(i);while (k--) {ret.push_back(p.top());p.pop();}return ret;} };

    Code - 優(yōu)先隊列(優(yōu)化 O(NlogK))(牛客)

    class Solution {vector<int> ret; public:vector<int> GetLeastNumbers_Solution(vector<int> &nums, int k) {// 注意越界條件if (nums.empty() || k <= 0 || k > nums.size()) return vector<int>();if (k == nums.size())return vector<int>(nums);// 注意使用堆與無優(yōu)化的方法不同,這里要使用最大堆priority_queue<int> p;// 先把前 K 個數(shù)壓入int i = 0;for (; i < k; i++)p.push(nums[i]);// 判斷后面的數(shù)for (; i < nums.size(); i++) {if (nums[i] < p.top()) {p.pop();p.push(nums[i]);}}// 導(dǎo)出結(jié)果while (!p.empty()) {ret.push_back(p.top());p.pop();}return ret;} };

    Code - 可以改變數(shù)組(牛客)

    class Solution {int partition(vector<int> &nums, int lo, int hi) {// 隨機選擇切分元素// srand(time(0));int base = rand() % (hi - lo + 1) + lo; // 隨機選擇 pivotswap(nums[base], nums[hi]); // 把 pivot 交換到末尾auto& pivot = nums[hi]; // 注意是引用int i = lo, j = hi; // j = hi-1; // errwhile (i < j) {while (nums[i] <= pivot && i < j) // 這里是求正序i++;while (nums[j] >= pivot && i < j) j--;if (i < j)swap(nums[i], nums[j]);}swap(nums[i], pivot);return i;} public:vector<int> GetLeastNumbers_Solution(vector<int> &nums, int k) {// 注意越界條件if (nums.empty() || k <= 0 || k > nums.size()) return vector<int>();if (k == nums.size())return vector<int>(nums);int lo = 0, hi = nums.size() - 1;int index = partition(nums, lo, hi);while(index != k-1) {if (index > k-1) {hi = index - 1;index = partition(nums, lo, hi);} else {lo = index + 1;index = partition(nums, lo, hi);}}return vector<int>(nums.begin(), nums.begin() + k);} };

    Code - 第 k 個最大的數(shù)(LeetCode)

    class Solution {int partition(vector<int>& nums, int lo, int hi) {int base = rand() % (hi - lo + 1) + lo; // 隨機選擇 pivotswap(nums[base], nums[hi]); // 把 pivot 交換到末尾auto& pivot = nums[hi]; // 注意是引用int i = lo, j = hi; // j = hi-1; // errwhile (i < j) {while (nums[i] >= pivot && i < j) // 這里是求逆序i++;while (nums[j] <= pivot && i < j)j--;if (i < j)swap(nums[i], nums[j]);}swap(nums[i], pivot);return i;}public:int findKthLargest(vector<int>& nums, int k) {if (nums.empty() || k < 0) return 0;int lo = 0;int hi = nums.size() - 1;int index = partition(nums, lo, hi);while (index != k - 1) {if (index > k - 1) {hi = index - 1;index = partition(nums, lo, hi);}else {lo = index + 1;index = partition(nums, lo, hi);}}return nums[k - 1];} };

    41. 數(shù)據(jù)流中的中位數(shù)(數(shù)據(jù)結(jié)構(gòu):堆)

    數(shù)據(jù)流中的中位數(shù) - NowCoder

    題目描述

    如何得到一個數(shù)據(jù)流中的中位數(shù)? 如果從數(shù)據(jù)流中讀出奇數(shù)個數(shù)值,那么中位數(shù)就是所有數(shù)值排序之后位于中間的數(shù)值。 如果從數(shù)據(jù)流中讀出偶數(shù)個數(shù)值,那么中位數(shù)就是所有數(shù)值排序之后中間兩個數(shù)的平均值。 我們使用Insert()方法讀取數(shù)據(jù)流,使用GetMedian()方法獲取當(dāng)前讀取數(shù)據(jù)的中位數(shù)。

    思路

    • 使用平衡二叉樹 AVL
      • 不推薦,因為一般沒有哪個語言會實現(xiàn)這個結(jié)構(gòu),它的綜合性能不如紅黑樹
    • 使用兩個堆:一個最大堆,一個最小堆
    • 保持兩個堆的大小平衡

    Code - 使用*_heap系列函數(shù)

    class Solution {// 用優(yōu)先隊列會更方便,這里試試使用 C++ 的 *_heap() 系列函數(shù)vector<int> left; // 最大堆vector<int> right; // 最小堆,最小堆中的元素都大于最大堆中的元素int N; // 記錄讀入的元素數(shù) public:Solution(): N(0) {} // 這一步不用也沒關(guān)系,默認會初始化為 0void Insert(int num) {N++; // 從 1 開始計數(shù)if (N & 1) { // 通過奇偶確保兩個堆中的元素數(shù)是平衡的// 如果是第奇數(shù)個就加入到 right// 為了保證 right 永遠大于 left,正確的添加方法是,// 先加入到 left,然后彈出 left 的堆頂元素加入到 rightleft.push_back(num);push_heap(left.begin(), left.end()); // push 后要重新調(diào)整堆,默認是最大堆num = left[0]; // 保存堆頂元素pop_heap(left.begin(), left.end()); // 在 pop 前,需要將堆頂元素移到末尾left.pop_back();right.push_back(num);push_heap(right.begin(), right.end(), greater<int>()); // 調(diào)整到最小堆,需要加入仿函數(shù)} else {// 如果是第偶數(shù)個就加入到左邊right.push_back(num);push_heap(right.begin(), right.end(), greater<int>());num = right[0];pop_heap(right.begin(), right.end(), greater<int>());right.pop_back();left.push_back(num);push_heap(left.begin(), left.end());}}double GetMedian() { if (N & 1) { // 如果是奇數(shù),那么中位數(shù)就是 right 的堆頂return (double)right[0];} else {return (double)(left[0] + right[0]) / 2;}} };

    Code - 使用優(yōu)先隊列

    class Solution {priority_queue<int> left; // 最大堆priority_queue<int, vector<int>, greater<int>> right; // 最小堆,最小堆中的元素都大于最大堆中的元素int N; // 記錄讀入的元素數(shù) public:Solution(): N(0) {} // 這一步不用也沒關(guān)系,默認會初始化為 0void Insert(int num) {N++; // 從 1 開始計數(shù)if(N & 1) { // 通過奇偶確保兩個堆中的元素數(shù)是平衡的// 如果是第奇數(shù)個就加入到 right// 為了保證 right 永遠大于 left,正確的添加方法是,// 先加入到 left,然后彈出 left 的堆頂元素加入到 rightleft.push(num);num = left.top();left.pop();right.push(num);} else { // 如果是第偶數(shù)個就加入到左邊right.push(num);num = right.top();right.pop();left.push(num);}}double GetMedian() { if (N & 1) { // 如果是奇數(shù),那么中位數(shù)就是 right 的堆頂return (double)right.top();} else {return (double)(left.top() + right.top()) / 2;}} };

    42. 連續(xù)子數(shù)組的最大和

    連續(xù)子數(shù)組的最大和 - NowCoder

    題目描述

    {6,-3,-2,7,-15,1,2,2},連續(xù)子數(shù)組的最大和為 8(從第 0 個開始,到第 3 個為止)

    思路

    • 因為不需要輸出路徑,所以不需要 DP
    • 法1)暴力枚舉-兩層循環(huán)
    • 法2)實際上,只要遍歷一次即可,首先用一個變量保存當(dāng)前的最大值
      • 如果當(dāng)前和 sum 為負數(shù),那么直接舍棄,用下一個值作為當(dāng)前和
      • 否則,加上下一個值(無論正負)
      • 與當(dāng)前最大值比較,保留最大的

    Code - 法1

    class Solution { public:int FindGreatestSumOfSubArray(vector<int>& array) {int _max = array[0]; // 存在全為負數(shù)的情況// 最安全的作法是賦值為數(shù)組的第一個數(shù)for (int i = 0; i < array.size(); i++) {int _sum = 0;for (int j = i; j < array.size(); j++) {_sum += array[j];_max = max(_max, _sum);}}return _max;} };

    Code - 法2

    class Solution { public:int FindGreatestSumOfSubArray(vector<int>& array) {if (array.empty()) return int();if (array.size() == 1) return array[0];int _max = array[0]; // 存在全為負數(shù)的情況// 最安全的作法是賦值為數(shù)組的第一個數(shù)int _sum = array[0];for (int i = 1; i < array.size(); i++) {if (_sum < 0) {_sum = array[i];} else {_sum += array[i];}_max = max(_sum, _max);}return _max;} };

    43. 從 1 到 n 整數(shù)中 1 出現(xiàn)的次數(shù)(Trick)

    整數(shù)中1出現(xiàn)的次數(shù)(從1到n整數(shù)中1出現(xiàn)的次數(shù)) - NowCoder

    數(shù)字 1 的個數(shù) - LeetCode

    題目描述

    給定一個整數(shù) n,計算所有小于等于 n 的非負整數(shù)中數(shù)字 1 出現(xiàn)的個數(shù)。

    思路

    • 暴力枚舉,時間復(fù)雜度 O(NlogN)
    • 找規(guī)律 O(logN)

    Code - 暴力枚舉

    class Solution {int numberOf1(int i) {int cnt = 0;while(i) {if(i % 10 == 1)cnt++;i /= 10;}return cnt;} public:// int countDigitOne(int n) { // LeetCodeint NumberOf1Between1AndN_Solution(int n) {int cnt = 0;for (int i=1; i<=n; i++)cnt += numberOf1(i);return cnt;} };
    • LeetCode 會超時

    Code - 找規(guī)律

    class Solution { public:// int countDigitOne(int n) { // LeetCodeint NumberOf1Between1AndN_Solution(int n) {int cnt = 0;for (long m=1; m<=n; m*=10) { // 注意這里用 int m 在 LeetCode 會越界int a = n/m;int b = n%m;cnt += (a+8) / 10 * m + (a%10==1)*(b+1);}return cnt;} };

    LeetCode 討論區(qū)

    44. 數(shù)字序列中的某一位數(shù)字(Trick)

    題目描述

    數(shù)字以 0123456789101112131415... 的格式序列化到一個字符串中,求這個字符串的第 index 位。 在這個序列中,第 5 位是 5(從 0 計數(shù)),第 13 位是 1,第 19 位是 4.

    思路

    • 暴力求解
      • 累加每個數(shù)的長度
    • Trick-類似二分的思想
      • 比如第 1001 位,
      • 0~9 的長度為 10——10 < 1001
      • 10~99 的長度為 180——10+180 < 1001
      • 100~999 的長度 為 2700——10+180+2700 > 1001
      • (1001 - 10 - 180) = 811 = 270*3 + 1
      • 100 + 270 = 370 的第 1 位(從 0 計數(shù)),即 7

    Code

    int countOfIntegers(int digits); int digitAtIndex(int index, int digits); int beginNumber(int digits);int digitAtIndex(int index) {if(index < 0)return -1;int digits = 1;while(true) {int numbers = countOfIntegers(digits);if(index < numbers * digits)return digitAtIndex(index, digits);index -= digits * numbers;digits++;}return -1; }int countOfIntegers(int digits) {if(digits == 1)return 10;int count = (int) std::pow(10, digits - 1);return 9 * count; }int digitAtIndex(int index, int digits) {int number = beginNumber(digits) + index / digits;int indexFromRight = digits - index % digits;for(int i = 1; i < indexFromRight; ++i)number /= 10;return number % 10; }int beginNumber(int digits) {if(digits == 1)return 0;return (int) std::pow(10, digits - 1); }

    45. 把數(shù)組排成最小的數(shù)(排序)

    把數(shù)組排成最小的數(shù) - NowCoder

    題目描述

    輸入一個正整數(shù)數(shù)組,把數(shù)組里所有數(shù)字拼接起來排成一個數(shù),打印能拼接出的所有數(shù)字中最小的一個。 例如輸入數(shù)組{3,32,321},則打印出這三個數(shù)字能排成的最小數(shù)字為321323。

    思路

    • 自定義排序
      • 在比較兩個字符串 S1 和 S2 的大小時,應(yīng)該比較的是 S1+S2 和 S2+S1 的大小,
      • 如果 S1+S2 < S2+S1,那么應(yīng)該把 S1 排在前面,否則應(yīng)該把 S2 排在前面。
    • 利用 stringstream 拼接數(shù)字
    • C++ 中 int 轉(zhuǎn) string 的函數(shù)
      • to_string()

    Code - 使用比較函數(shù)

    class Solution {static bool cmp(const int &l, const int &r) {string ll = to_string(l) + to_string(r);string rr = to_string(r) + to_string(l);return ll < rr;} public:string PrintMinNumber(vector<int> numbers) {sort(numbers.begin(), numbers.end(), cmp);stringstream ss;for (auto i : numbers) ss << to_string(i);return ss.str();} };

    Code - 使用Lambda表達式

    class Solution { public:string PrintMinNumber(vector<int> numbers) {sort(numbers.begin(), numbers.end(), [](const int &l, const int &r){return to_string(l) + to_string(r) < to_string(r) + to_string(l)});stringstream ss;for (auto i : numbers) ss << to_string(i);return ss.str();} };

    46. 把數(shù)字翻譯成字符串(解碼方法)(動態(tài)規(guī)劃)

    解碼方法 - LeetCode

    題目描述

    給定一個數(shù)字,按照如下規(guī)則翻譯成字符串:1 翻譯成“a”,2 翻譯成“b”... 26 翻譯成“z”。 給定一個只包含數(shù)字的非空字符串,請計算解碼方法的總數(shù)。
    • 劍指Offer 上是數(shù)字范圍為 0~25,其他一致

    思路

    • 動態(tài)規(guī)劃
    • 遞推公式dp[0] = 1 dp[1] = 0 int(s[0]) == 0= 1 其他 dp[i] += dp[i-1] int(s[i-1: i]) != 0 && int(s[i-2: i]) > 26+= dp[i-1] + dp[i-2] int(s[i-1: i]) != 0 && int(s[i-2: i]) <= 26

    Code(Python)

    class Solution:def numDecodings(self, s):""":type s: str:rtype: int"""if len(s) < 1: return 0n = len(s)dp = [0] * (n + 1)dp[0] = 1 # 注意初始化 dp[0] = 1dp[1] = 1 if s[0] != '0' else 0for i in range(2, n+1):if int(s[i-1]) != 0:dp[i] += dp[i-1]if int(s[i-2]) == 0:continueif int(s[i-2: i]) <= 26:dp[i] += dp[i-2]return dp[n]

    47. 禮物的最大價值(年終獎)(動態(tài)規(guī)劃)

    年終獎_牛客網(wǎng)

    題目描述

    在一個 m*n 的棋盤的每一個格都放有一個禮物,每個禮物都有一定價值(大于 0)。 從左上角開始拿禮物,每次向右或向下移動一格,直到右下角結(jié)束。 給定一個棋盤,求拿到禮物的最大價值。例如,對于如下棋盤1 10 3 812 2 9 65 7 4 113 7 16 5 禮物的最大價值為 1+12+5+7+7+16+5=53。

    思路

    • 深度優(yōu)先搜索-復(fù)雜度大
    • 動態(tài)規(guī)劃
    • 二維遞推公式初始化 dp[0][0] = board[0][0] dp[i][0] = dp[i-1][0] + board[i][0] dp[0][j] = dp[0][j-1] + board[0][j]dp[i][j] = max(dp[i-1][j], dp[i][j-1]) + board[i][j]
      • 注意邊界條件
    • 一維遞推公式(優(yōu)化版)TODO

    Code - 二維DP

    class Bonus { public:int getMost(vector<vector<int>>& board) {if (board.empty() || board[0].empty())return 0;int m = board.size();int n = board[0].size();vector<vector<int>> dp(m, vector<int>(n, 0));dp[0][0] = board[0][0];for (int i=1; i<m; i++)dp[i][0] = dp[i-1][0] + board[i][0];for (int j=1; j<n; j++)dp[0][j] = dp[0][j-1] + board[0][j];for (int i=1; i<m; i++) {for (int j=1; j<n; j++) {dp[i][j] = max(dp[i-1][j], dp[i][j-1]) + board[i][j];}}return dp[m-1][n-1];} };

    Code - 一維DP

    class Bonus { public:int getMost(vector<vector<int>>& board) {if (board.empty() || board[0].empty())return 0;int n = board[0].size();vector<int> dp(n, 0); // 注意不是 dp(0, n)for (auto& v: board) {dp[0] += v[0];for (int i=1; i<n; i++)dp[i] = max(dp[i], dp[i-1]) + v[i];}return dp[n-1];} };

    48. 最長不含重復(fù)字符的子字符串(動態(tài)規(guī)劃)

    題目描述

    輸入一個字符串(只包含 a~z 的字符),求其最長不含重復(fù)字符的子字符串的長度。 例如對于 arabcacfr,最長不含重復(fù)字符的子字符串為 acfr,長度為 4。

    思路

    • 暴力枚舉,時間復(fù)雜度 O(N^3),如果使用 set 的結(jié)構(gòu),可以降到 O(N^2)
    • 動態(tài)規(guī)劃
    • 遞推思路記:dp[i] := 以 s[i] 結(jié)尾的最長不重復(fù)子串注意:這并不是全局最長的結(jié)果,全局最長用另一個變量保存 遞推公式:dp[i] = dp[i-1] + 1 s[i] 之前沒有出現(xiàn)過= d s[i] 出現(xiàn)過,d == 兩次出現(xiàn)之間的距離,且 d <= dp[i-1]= dp[i-1] + 1 s[i] 出現(xiàn)過,d == 兩次出現(xiàn)之間的距離,但 d > dp[i-1]

    Code - DP

    int longestSubstringWithoutDuplication(const string& s) {if (s.length() < 1) return 0;int n = s.length();int maxLen = 0;vector<int> dp(n, 1); // 長度至少為 1vector<int> book(26, -1); // 模擬字典// dp[0] = 1;book[s[0] - 'a'] = 0;for (int i=1; i < n; i++) {int pre = book[s[i] - 'a'];if (pre < 0 || i - pre > dp[i-1]) {dp[i] = dp[i-1] + 1;maxLen = max(dp[i], maxLen);}else {maxLen = max(dp[i-1], maxLen);dp[i] = i - pre;}book[s[i] - 'a'] = i;}return maxLen; }int main() {cout << longestSubstringWithoutDuplication("abcacfrar") << endl; // 4cout << longestSubstringWithoutDuplication("acfrarabc") << endl; // 4cout << longestSubstringWithoutDuplication("arabcacfr") << endl; // 4cout << longestSubstringWithoutDuplication("aaaa") << endl; // 1 cout << longestSubstringWithoutDuplication("abcdefg") << endl; // 7 cout << longestSubstringWithoutDuplication("") << endl; // 0cout << longestSubstringWithoutDuplication("aaabbbccc") << endl; // 2cout << longestSubstringWithoutDuplication("abcdcba") << endl; // 4cout << longestSubstringWithoutDuplication("abcdaef") << endl; // 6return 0; }

    Code - 優(yōu)化

    int longestSubstringWithoutDuplication(const std::string& s) {if (s.length() < 1) return 0;int n = s.length();int curLen = 0;int maxLen = 0;vector<int> book(26, -1); // 模擬字典for (int i=0; i < n; i++) {int pre = book[s[i] - 'a'];if (pre < 0 || i - pre > curLen) {curLen++;maxLen = max(curLen, maxLen);}else {maxLen = max(curLen, maxLen);curLen = i - pre;}book[s[i] - 'a'] = i;}return maxLen; }int main() {cout << longestSubstringWithoutDuplication("abcacfrar") << endl; // 4cout << longestSubstringWithoutDuplication("acfrarabc") << endl; // 4cout << longestSubstringWithoutDuplication("arabcacfr") << endl; // 4cout << longestSubstringWithoutDuplication("aaaa") << endl; // 1 cout << longestSubstringWithoutDuplication("abcdefg") << endl; // 7 cout << longestSubstringWithoutDuplication("") << endl; // 0cout << longestSubstringWithoutDuplication("aaabbbccc") << endl; // 2cout << longestSubstringWithoutDuplication("abcdcba") << endl; // 4cout << longestSubstringWithoutDuplication("abcdaef") << endl; // 6return 0; }

    49. 丑數(shù)(動態(tài)規(guī)劃)

    丑數(shù) - NowCoder

    題目描述

    把只包含質(zhì)因子2、3和5的數(shù)稱作丑數(shù)(Ugly Number)。 例如6、8都是丑數(shù),但14不是,因為它包含質(zhì)因子7。 習(xí)慣上我們把1當(dāng)做是第一個丑數(shù)。求按從小到大的順序的第N個丑數(shù)。

    思路

    • 動態(tài)規(guī)劃

    Code

    class Solution { public:int GetUglyNumber_Solution(int n) {// if (n <=6 ) return n;vector<int> dp(n+1, 0); // dp[0] = 0;int i2=1, i3=1, i5=1;dp[1] = 1;for (int i=2; i<=n; i++) {int nxt2 = dp[i2] * 2;int nxt3 = dp[i3] * 3;int nxt5 = dp[i5] * 5;dp[i] = min({nxt2, nxt3, nxt5});// 注意以下不能使用 else 結(jié)構(gòu),因為可能存在 nxtM == nxtN 的情況if (dp[i] == nxt2) i2++;if (dp[i] == nxt3) i3++;if (dp[i] == nxt5) i5++;}return dp[n];} };

    50.1 第一個只出現(xiàn)一次的字符位置(Hash)

    第一個只出現(xiàn)一次的字符 - NowCoder

    題目描述

    在一個字符串 (1 <= 字符串長度 <= 10000,全部由字母組成) 中找到第一個只出現(xiàn)一次的字符,并返回它的位置。

    思路

    • Hash 表
    • 因為是字符,可以使用數(shù)組模擬哈希表

    Code - 數(shù)組

    class Solution { public:int FirstNotRepeatingChar(const string& s) {vector<int> m(256, 0);for (auto c : s)if (m[c] < 1)m[c] = 1;elsem[c] += 1;for (int i=0; i < s.length(); i++)if (m[s[i]] == 1)return i;return -1;} };

    Code - Hash(C++ map)

    class Solution {map<char, int> m; public:int FirstNotRepeatingChar(const string& s) {for (auto c : s)if (m.count(c) < 1)m[c] = 1;elsem[c] += 1;for (int i=0; i < s.length(); i++)if (m[s[i]] == 1)return i;return -1;} };

    Code - Hash(Python dict)

    class Solution:def FirstNotRepeatingChar(self, s):d = dict()for c in s:if c in d:d[c] += 1else:d[c] = 1for i in range(len(s)):if d[s[i]] == 1:return ireturn -1

    50.2 字符流中第一個只出現(xiàn)一次的字符(數(shù)據(jù)結(jié)構(gòu):隊列)

    字符流中第一個不重復(fù)的字符 - NowCoder

    題目描述

    請實現(xiàn)一個函數(shù)用來找出字符流中第一個只出現(xiàn)一次的字符。 例如,當(dāng)從字符流中只讀出前兩個字符"go"時,第一個只出現(xiàn)一次的字符是"g"。 當(dāng)從該字符流中讀出前六個字符“google"時,第一個只出現(xiàn)一次的字符是"l"。

    思路

    • 計數(shù)排序——使用一個數(shù)組數(shù)組保存出現(xiàn)元素的次數(shù)
    • 使用隊列保存出現(xiàn)的元素

    Code - 隊列

    class Solution {int book[256];queue<char> q;public:Solution() {fill(book, book + 256, 0); // 初始化,實際不需要這步,默認全部初始化為 0}void Insert(char ch) {book[ch] += 1;q.push(ch);while (!q.empty() && book[q.front()] > 1) {q.pop();}}char FirstAppearingOnce() {return q.empty() ? '#' : q.front();} };

    51. 數(shù)組中的逆序?qū)?/h2>

    數(shù)組中的逆序?qū)?- NowCoder

    題目描述

    在數(shù)組中的兩個數(shù)字,如果前面一個數(shù)字大于后面的數(shù)字,則這兩個數(shù)字組成一個逆序?qū)Α?輸入一個數(shù)組,求出這個數(shù)組中的逆序?qū)Φ目倲?shù)P。并將P對1000000007取模的結(jié)果輸出。 即輸出P%1000000007

    總結(jié)

    以上是生活随笔為你收集整理的算法工程师笔试 -剑指offer-习题详细解答的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。

    99久热在线精品视频观看 | 久久久久久亚洲精品 | 国产无套一区二区三区久久 | 日韩在线观看你懂的 | 欧美最猛性xxxxx免费 | 国产成人精品久久亚洲高清不卡 | 国产a免费 | av亚洲产国偷v产偷v自拍小说 | 国产小视频在线免费观看视频 | 最近中文字幕高清字幕免费mv | 久久男人视频 | 免费av电影网站 | 麻豆视频在线播放 | 亚洲欧洲一级 | 国产精品一区二区三区在线免费观看 | av高清网站在线观看 | 91精品久久久久久综合乱菊 | 色婷婷激情电影 | 国产精品美女免费视频 | a色视频| 国产小视频在线免费观看 | 欧美乱熟臀69xxxxxx | 午夜国产一区二区 | 午夜国产福利在线观看 | www.黄色片.com| 国产成人久久精品77777综合 | 久久久精品免费观看 | 亚洲成人动漫在线观看 | 日韩免费三级 | 国产一区网 | 久久高清 | 麻豆系列在线观看 | 麻豆免费在线播放 | 操操操夜夜操 | 国产福利一区二区三区视频 | 精品国产一区二区三区久久久蜜月 | 久久高清免费观看 | 亚洲成av人影院 | 午夜在线观看影院 | 日韩精品在线视频免费观看 | 国产中文在线字幕 | 国产三级午夜理伦三级 | 日韩理论在线播放 | 日本三级不卡视频 | 一区二区三区高清在线 | 久草国产在线 | 国产麻豆精品久久一二三 | 欧美在线视频日韩 | 久久精品国产精品亚洲 | 99久久国产免费免费 | 国产成人三级一区二区在线观看一 | 91亚洲精品视频 | 亚洲夜夜综合 | 天天摸日日摸人人看 | 91av视频导航| 最新免费av在线 | 久久综合久久综合久久 | 日本黄色一级电影 | 国产麻豆精品久久一二三 | 999超碰 | 91成年人视频 | 偷拍久久久 | 九草在线观看 | 四虎视频| 日韩小视频网站 | 日韩免费中文字幕 | 粉嫩一区二区三区粉嫩91 | www91在线观看 | 婷色在线 | 亚洲区精品| 99精品乱码国产在线观看 | 色资源网在线观看 | 免费国产在线视频 | 国产精品第三页 | 91观看视频 | av看片在线观看 | 91精品影视 | 国产一区二区三区免费在线观看 | 国产精品久久久免费 | 亚洲成人中文在线 | 国产乱对白刺激视频在线观看女王 | 久久免费电影 | 日韩a级免费视频 | 免费的黄色av | 91麻豆国产福利在线观看 | 99久久精品日本一区二区免费 | 久久久99国产精品免费 | 久精品在线| 国产成人亚洲精品自产在线 | 亚洲 欧美 国产 va在线影院 | 久二影院| 国产美女无遮挡永久免费 | 国产成人黄色网址 | 欧美另类高潮 | 欧美成人精品在线 | 五月天婷婷在线视频 | 成人四虎| 国产精品国产三级国产 | 国产亚洲精品久久久久久电影 | 国产精品原创av片国产免费 | 在线观看网站黄 | 国内综合精品午夜久久资源 | 国产精品久久久久一区二区 | 久久午夜影视 | av一级片在线观看 | 国产黄色片免费 | 精品毛片一区二区免费看 | 久久私人影院 | 五月天久久久久 | 国产精品成人免费精品自在线观看 | 欧美日韩在线视频一区二区 | 成人a免费视频 | 热99在线| 久久久久久久久电影 | 日韩电影黄色 | 日韩精品视频在线观看免费 | 一级黄色大片在线观看 | 亚洲精品免费在线观看视频 | 成年人黄色大片在线 | 99热在线免费观看 | 香蕉影视在线观看 | 天天摸夜夜添 | 国产成人福利在线 | 日韩三级不卡 | 91九色综合 | 欧美久久久一区二区三区 | 亚洲视频播放 | 欧美另类老妇 | 免费观看的av网站 | 中文字幕一区二区三区乱码不卡 | 日韩精品不卡在线观看 | 一级黄视频 | 天天摸夜夜操 | 麻豆免费精品视频 | www.操.com| 国产中文字幕在线看 | 伊人射| 成片免费观看视频 | 香蕉影院在线观看 | 国产成人精品一区二区三区福利 | 狠狠色丁香婷婷综合久久片 | 欧美性生爱| 91中文字幕网| 国产一区二区三区高清播放 | 五月婷在线观看 | 黄色网址在线播放 | 国产又粗又猛又黄又爽视频 | 成人欧美日韩国产 | 亚洲精品黄 | 91在线资源 | 中文字幕欧美日韩va免费视频 | 欧美午夜一区二区福利视频 | 福利在线看片 | 91精品婷婷国产综合久久蝌蚪 | 波多野结衣在线视频一区 | 91日韩精品一区 | 永久免费精品视频 | 精品在线看 | 91资源在线观看 | av免费在线观看网站 | 午夜性福利 | 福利电影久久 | 日日干美女 | 久久66热这里只有精品 | 99久在线精品99re8热视频 | 久久久久国产精品厨房 | 在线草 | 成人久久| 视频国产一区二区三区 | 久草在线最新 | av免费观看网站 | 中文字幕色在线 | 精品人人爽 | 国产高清不卡一区二区三区 | 亚洲高清在线观看视频 | 国产精品免费在线观看视频 | 乱子伦av| 国产无套一区二区三区久久 | 特黄免费av | 美女福利视频一区二区 | 狠狠狠狠狠狠操 | 午夜精品久久久久久久久久久 | 亚洲免费激情 | av大片免费 | 国产亚洲一区二区在线观看 | 亚洲视屏一区 | 成人免费一区二区三区在线观看 | 免费人成在线观看网站 | 国产欧美日韩精品一区二区免费 | 麻豆视频网址 | 国产视频精选 | 国产精品久久久精品 | 色婷婷激情五月 | 亚洲精品国偷拍自产在线观看蜜桃 | 久久爱www.| 亚洲视频axxx | 91中文字幕网 | 99久久精品午夜一区二区小说 | aaa日本高清在线播放免费观看 | 中文字幕一区2区3区 | 成人毛片一区二区三区 | 日韩欧美精品免费 | 精品久久久久免费极品大片 | 99久久电影 | 日日夜夜网 | 国产高清视频免费观看 | 国产精品ssss在线亚洲 | 在线精品视频免费播放 | 91久久久国产精品 | 成人蜜桃网 | 欧美精品乱码久久久久 | 青青久草在线 | 波多野结衣在线观看视频 | 久久在线 | 97成人免费视频 | 久精品视频| 四虎永久免费在线观看 | 午夜在线免费视频 | 成人精品影视 | 91探花国产综合在线精品 | 亚洲天堂自拍视频 | 精品国产一区二区三区免费 | 中文字幕乱码电影 | 激情av资源 | 午夜私人影院 | 国产精品久久久久久一区二区 | 国产精久久久 | 亚洲综合欧美激情 | 亚洲另类人人澡 | 夜夜夜草 | 亚洲一级国产 | 毛片永久新网址首页 | 免费福利在线观看 | 免费色视频 | 久久爱资源网 | 日韩亚洲在线视频 | 久久亚洲热 | 美女视频黄免费网站 | 国产v在线观看 | 18做爰免费视频网站 | 激情av资源 | 婷婷色综合色 | 91在线视频精品 | 日批视频在线 | 日韩免费观看av | 视频在线观看一区 | 精品美女国产在线 | 成人一区二区在线观看 | 中文字幕第一 | 99热只有精品在线观看 | 午夜久久福利影院 | 一区二区欧美在线观看 | 97人人网| 一级免费观看 | 色婷婷综合五月 | 亚洲国产日韩一区 | 99精品视频免费在线观看 | 蜜臀aⅴ国产精品久久久国产 | 99亚洲精品 | 中国一 片免费观看 | 国产精品日韩欧美一区二区 | 西西大胆免费视频 | 欧美精品久久久久久久免费 | 日日操夜| 91精品久久久久久久91蜜桃 | 日韩美在线 | 一区二区三区电影在线播 | 欧美日韩高清在线一区 | 99免费精品视频 | 视频二区在线 | 中午字幕在线 | 婷婷丁香激情网 | 91高清免费 | 午夜在线免费观看视频 | 四虎在线免费观看 | 成人在线网站观看 | 亚州精品天堂中文字幕 | 欧美精品中文在线免费观看 | 超碰国产在线观看 | 亚洲精品高清视频 | 国产视频在线播放 | 久久综合之合合综合久久 | 中文字幕高清视频 | 日日碰夜夜爽 | 日韩免费看的电影 | 国产黄色大片 | 国产真实精品久久二三区 | 又黄又爽又无遮挡的视频 | 日韩欧美高清在线 | 一区二区三区在线免费播放 | 国产精品 欧美 日韩 | 四虎成人精品在永久免费 | 成人蜜桃视频 | 亚洲成人国产 | 久草观看 | 夜夜爽天天爽 | 免费看片在线观看 | 波多野结衣一区三区 | 最近中文字幕 | 日韩网站在线播放 | 91av视频在线播放 | 日本h视频在线观看 | 久久久在线免费观看 | 精品欧美日韩 | 日韩a在线看 | 9ⅰ精品久久久久久久久中文字幕 | 99婷婷 | 日韩有码中文字幕在线 | 亚洲精品国产精品乱码在线观看 | 色多多视频在线 | 91精品久久久久久 | 91九色视频在线观看 | 最新免费av在线 | 中文字幕国产亚洲 | 国产中文字幕在线免费观看 | 久久精品99国产精品酒店日本 | 欧美黑吊大战白妞欧美 | 欧美一级专区免费大片 | 18国产精品白浆在线观看免费 | 99精品影视 | 国产一区免费在线观看 | 97av影院| 免费观看午夜视频 | 女人18毛片90分钟 | 久草网视频在线观看 | 欧美日本在线观看视频 | 婷婷亚洲综合五月天小说 | 亚洲欧美视频网站 | 成人午夜网址 | 特级黄色视频毛片 | 国产成人精品一区在线 | 超碰成人免费电影 | 国产原创中文在线 | 亚洲aⅴ久久精品 | 九九综合九九综合 | 国产精品一区二区麻豆 | a级国产乱理论片在线观看 伊人宗合网 | 黄色日本免费 | 中文字幕乱码电影 | 精品久久久久久久 | 国产九色视频在线观看 | 亚洲视屏在线播放 | 亚洲国产999| 国产97免费| 色婷婷福利视频 | 日韩精品极品视频 | 久久视频在线观看 | 成人av免费看 | 亚洲午夜av| 国产 日韩 中文字幕 | 国产精品久久久久久久久久久免费看 | 免费av网址在线观看 | 久草视频在线免费看 | 97成人精品视频在线播放 | 欧美日韩中文在线观看 | 国产精品久久在线观看 | 国产精品久久久久久婷婷天堂 | 99国产精品久久久久老师 | 成人av一区二区在线观看 | 丁香六月久久综合狠狠色 | 国产一级在线观看 | 色综合久久88色综合天天人守婷 | 激情综合啪 | 亚洲精品小区久久久久久 | 午夜精品久久一牛影视 | 精品国产伦一区二区三区观看说明 | 超碰97免费在线 | 久久综合给合久久狠狠色 | 99视频导航 | 超碰免费成人 | 国产小视频免费在线网址 | 免费a v视频 | 天天爽天天碰狠狠添 | 国产精品欧美久久久久无广告 | 三级黄色片在线观看 | 久久精品久久久精品美女 | 91.精品高清在线观看 | 亚洲欧美经典 | 天天躁日日躁狠狠躁av麻豆 | 国产精品淫 | 麻豆小视频在线观看 | 在线91网| 91精品国产91热久久久做人人 | 4p变态网欧美系列 | 很黄很色很污的网站 | 国产精品久久久av久久久 | 99国产精品免费网站 | 亚洲欧洲精品一区二区精品久久久 | 日韩在线观看一区二区三区 | 欧美精品一区二区在线观看 | 国产精品免费观看网站 | 久久99精品久久久久久 | 菠萝菠萝在线精品视频 | 色婷婷播放| 久久高清片 | 成人av免费在线 | 免费亚洲一区二区 | 亚洲三级av | 97碰在线视频| 亚洲国产精品一区二区久久hs | 久久免费黄色网址 | 9色在线视频 | 肉色欧美久久久久久久免费看 | 日韩1级片 | 日韩在线视频免费播放 | 欧美一区中文字幕 | 国产美女精品人人做人人爽 | av综合网址 | 日日夜夜精品免费视频 | 午夜12点| 中文字幕一区二区三区在线播放 | 久久综合给合久久狠狠色 | 亚洲aⅴ在线观看 | 精品一区二区6 | 91精品在线免费 | 亚洲免费专区 | 国产精品美女久久久网av | 欧美精品久久久久久久 | 亚洲国产精品va在线 | av在线a | 免费a网址 | 麻花传媒mv免费观看 | 蜜臀aⅴ国产精品久久久国产 | 天天干 天天摸 天天操 | 日本一区二区三区免费观看 | 国产精品网站一区二区三区 | 国产精品一区二区在线 | 麻豆国产精品va在线观看不卡 | 日韩91在线 | 国产精品一码二码三码在线 | 欧美精品v国产精品v日韩精品 | 日韩视频在线不卡 | 久久综合久久伊人 | 在线播放av网址 | 亚洲国产精彩中文乱码av | 日日婷婷夜日日天干 | 日日夜夜天天综合 | 天天综合网久久综合网 | 久久狠狠干 | 日韩免费在线观看网站 | 91精品毛片| 伊人国产在线播放 | 免费观看的黄色 | 成人欧美日韩国产 | 亚洲国产精品成人女人久久 | 蜜臀久久99静品久久久久久 | 973理论片235影院9 | 91视视频在线直接观看在线看网页在线看 | 黄色中文字幕在线 | 丁香六月婷 | 人人澡超碰碰97碰碰碰软件 | 亚洲综合网站在线观看 | 亚洲在线网址 | 欧美日韩一级视频 | 天天天操操操 | 97在线看片 | 日韩一级成人av | 91在线操 | 97国产情侣爱久久免费观看 | 久久午夜电影 | 国产1级视频 | 亚洲精品字幕在线观看 | 日日夜夜天天人人 | 欧美a视频在线观看 | 日韩一级网站 | 亚洲激情久久 | 久久最新 | 一区二精品 | 婷婷在线精品视频 | av色图天堂网| 久久香蕉一区 | 成人黄色在线观看视频 | 亚洲高清在线观看视频 | 国产高清在线不卡 | 欧洲亚洲精品 | 国产精品亚洲综合久久 | 久草网视频在线观看 | 狠狠色狠狠色合久久伊人 | 又黄又爽又刺激视频 | 国产日韩中文在线 | 超碰av在线播放 | 国产精品自产拍在线观看中文 | 国产黄色片免费看 | 久久精品国产一区 | 欧美一级黄色视屏 | 成人h动漫精品一区二 | 97人人模人人爽人人喊中文字 | 91片黄在线观 | 午夜在线国产 | 日韩欧美精品在线观看 | 久久久久久久久久久久久9999 | 久久久精品国产免费观看同学 | 欧美日韩免费看 | 亚洲自拍av在线 | 国产精品嫩草影院99网站 | 国产免费久久久久 | 国产精品s色 | 亚洲精品免费在线播放 | 日韩美女黄色片 | 国产成人精品av久久 | 日韩在线精品一区 | 超碰人人超 | 高清av中文在线字幕观看1 | 亚洲精品在线观看免费 | 日韩午夜剧场 | 91精品久 | 久久久免费精品视频 | 天堂在线视频中文网 | 久久精品永久免费 | 久草在线免费色站 | 久久精品视频在线免费观看 | 久久精品99久久久久久2456 | 日日噜噜噜噜夜夜爽亚洲精品 | 亚洲v欧美v国产v在线观看 | 五月天婷婷狠狠 | 日韩精品不卡 | 亚洲视频电影在线 | av国产网站 | 狠狠亚洲 | 国产一级91 | 亚洲婷久久 | 成人资源在线 | av3级在线 | 国产成人l区 | 成人动态视频 | 精品久久久久久久久久久久久 | 欧美怡红院视频 | 日韩av高清| 国内精品久久天天躁人人爽 | 日韩av中文在线观看 | 成片人卡1卡2卡3手机免费看 | 久久免费公开视频 | 精品视频久久 | av片在线观看免费 | 狠狠色伊人亚洲综合网站色 | 久久精品国产一区二区三区 | 在线91播放 | 亚洲人成在线电影 | 国内精品视频久久 | 五月花激情 | 免费成人在线视频网站 | 国产精品久久久影视 | 久久精品精品电影网 | 激情婷婷久久 | 国产真实精品久久二三区 | 在线免费观看av网站 | 亚洲国产精品一区二区尤物区 | 麻豆免费在线播放 | 免费在线观看午夜视频 | 国产成人精品一区二 | 国产成人精品日本亚洲999 | 国产午夜精品免费一区二区三区视频 | 国产成人61精品免费看片 | 一级黄色av | 国产日韩欧美视频在线观看 | 亚洲第一区在线播放 | 成人在线视频一区 | 美腿丝袜av | av观看免费在线 | 在线亚洲免费视频 | 91原创在线观看 | 免费观看久久久 | 日女人电影 | 成人在线网站观看 | 91免费试看| 91免费观看视频网站 | 99c视频高清免费观看 | 午夜免费久久看 | 激情五月六月婷婷 | 久热电影 | a视频在线 | 亚洲综合在线观看视频 | 激情综合网五月婷婷 | 97夜夜澡人人爽人人免费 | 麻豆视频免费入口 | 啪啪激情网| 欧洲一区二区在线观看 | 欧美日韩中文在线视频 | 18性欧美xxxⅹ性满足 | 天天做天天爱夜夜爽 | 中文字幕一区二区三区在线观看 | 色婷婷播放| 免费观看性生交大片3 | 96精品在线| 97在线观看视频 | 91精品在线播放 | 亚洲人在线 | 九九免费观看全部免费视频 | 日韩素人在线观看 | 日韩中文免费视频 | 精品久久久久一区二区国产 | 午夜电影中文字幕 | 最新中文字幕视频 | 中文av网| 又黄又爽又色无遮挡免费 | 91在线免费视频 | 国产香蕉久久 | 午夜精品久久久久久久99热影院 | 久久精品视频一 | 欧美91精品国产自产 | 久久高清免费视频 | 久艹在线观看视频 | 欧美日本啪啪无遮挡网站 | 欧美另类色图 | 午夜天使| 免费日韩电影 | 国产二区电影 | 免费av在线网 | 午夜精品福利一区二区三区蜜桃 | 日韩v在线91成人自拍 | 成人在线视频在线观看 | 亚洲男模gay裸体gay | 黄色一级免费网站 | 91桃色免费视频 | 成年人视频在线免费 | 日韩电影在线观看一区二区三区 | 91黄站| www四虎影院 | 久久综合久久久 | 久久99在线观看 | 91大神精品视频 | 亚洲精品av在线 | 在线观看国产91 | 日韩精品三区四区 | 九九九九热精品免费视频点播观看 | 综合中文字幕 | 欧美精品久久久久性色 | japanese黑人亚洲人4k | 天天五月天色 | 亚洲精品高清一区二区三区四区 | 手机av在线网站 | 久久久黄色av | 欧美性色综合 | 色婷婷激情电影 | 久久久综合色 | 国产亚洲精品久久久网站好莱 | 99精品国产一区二区三区麻豆 | 国产经典 欧美精品 | 久爱精品在线 | 一本一道波多野毛片中文在线 | 色偷偷网站视频 | 国产一级免费观看视频 | 国产精久久久久久久 | av在线播放免费 | 国产精品九九视频 | 久久久精品国产免费观看同学 | 欧美色插| 国产黄色在线网站 | 2017狠狠干 | 亚洲精品美女久久久久 | 奇米先锋 | 精品999久久久 | 欧美另类69 | 亚洲综合视频在线 | 91毛片在线 | 国产h在线观看 | 国产一区二区视频在线 | 日韩欧美69 | 亚洲欧美日韩在线看 | 成人aaa毛片 | 日日摸日日碰 | 91精品黄色| 探花视频网站 | 91精品一区在线观看 | 在线高清av | 成人性生交视频 | 2021国产视频| 超碰在线人人爱 | 黄色av电影网 | 国产亚洲精品久久久久久久久久 | 天天草天天草 | 日韩国产精品一区 | 亚洲四虎在线 | 在线观看你懂的网站 | 国产91在线观看 | 在线视频区 | 视频在线观看91 | 九色在线 | av 一区 二区 久久 | 激情视频免费观看 | 国产精品少妇 | 天天干夜夜爽 | 精品亚洲国产视频 | av黄色大片 | 在线观看视频精品 | 国产精品免费久久 | avwww在线| 欧美一级xxxx | 国产最新视频在线观看 | 免费能看的av | 欧美肥妇free | 成 人 免费 黄 色 视频 | 日韩丝袜 | 午夜视频黄 | 五月天婷亚洲天综合网精品偷 | 久久综合国产伦精品免费 | 国产a国产a国产a | 亚洲人成网站精品片在线观看 | 免费观看一级特黄欧美大片 | 日韩电影在线观看中文字幕 | 欧美一二三视频 | av在线免费观看黄 | 成人av电影免费 | 中文字幕在线久一本久 | 人人爽久久久噜噜噜电影 | 国产在线观看中文字幕 | 黄色一级在线免费观看 | 国产亚州精品视频 | 亚洲三级毛片 | 日韩中文字幕91 | 69国产成人综合久久精品欧美 | 国产高清成人在线 | 国产日韩欧美视频在线观看 | 亚洲最大av | 中文字幕在线观看网址 | 国产又粗又猛又色又黄视频 | 综合色伊人| 久久综合一本 | 极品嫩模被强到高潮呻吟91 | 狠狠躁夜夜躁人人爽超碰97香蕉 | 欧美精品二区 | 91亚洲狠狠婷婷综合久久久 | 视频一区二区三区视频 | 九色91在线| 欧美9999 | 亚洲精品网站在线 | 国产精品毛片一区视频播不卡 | 亚洲天堂社区 | 成人a大片 | 精品一区三区 | 亚州天堂 | 中文在线a∨在线 | 久久激情视频免费观看 | 亚洲婷婷免费 | 国产亚洲片 | 亚洲精选视频在线 | 深爱激情五月综合 | 久久污视频 | 日韩三级在线观看 | 久久久在线免费观看 | 成人网页在线免费观看 | 草久久av| www黄色com| 久久久久麻豆 | www.在线观看视频 | 久久免费毛片 | 91精品中文字幕 | 免费瑟瑟网站 | 日韩av在线高清 | 99热播精品| 欧美日韩免费网站 | 少妇超碰在线 | 国产精品一区免费看8c0m | 五月婷婷久久综合 | 国产中文字幕在线免费观看 | 婷婷电影在线观看 | 欧洲一区二区在线观看 | 久久免费毛片视频 | 人人插人人 | 成人黄色大片在线免费观看 | 欧美精品免费视频 | 青春草视频在线播放 | 久久国产精品久久w女人spa | 亚洲一区网站 | 久久手机精品视频 | a级国产乱理论片在线观看 特级毛片在线观看 | 一级片视频在线 | 久久精品国产免费看久久精品 | 伊人成人精品 | 天天干com | 91精品国产乱码在线观看 | 免费a视频在线观看 | 综合亚洲视频 | 免费看日韩片 | 国产精品久久久久久欧美 | 九九久久久久久久久激情 | 欧美午夜精品久久久久久孕妇 | 中文字幕在线观看2018 | 国产一区久久久 | 91网在线看 | 日韩av电影手机在线观看 | 黄色h在线观看 | 99视频免费播放 | av成人免费网站 | 久久精品91久久久久久再现 | 在线观看亚洲a | 99精品久久久 | 亚洲午夜久久久久久久久久久 | 亚洲人成网站精品片在线观看 | 国产一级黄 | 国产区精品区 | 国产在线视频资源 | 狠狠狠狠干 | 婷婷在线色 | 欧美精品在线一区 | 久草久草视频 | 国产精品丝袜在线 | 在线观看日韩一区 | 亚洲成a人片在线观看中文 中文字幕在线视频第一页 狠狠色丁香婷婷综合 | 97在线免费视频观看 | 亚洲视频网站在线观看 | 亚洲精品视频二区 | 一区二区三区在线观看免费视频 | 成人福利在线播放 | 亚洲欧美日韩精品久久奇米一区 | 最新国产在线 | 丰满少妇久久久 | 91精品国产92久久久久 | 亚洲成人精品在线 | 蜜桃久久久| 一区二区三区精品在线视频 | 色999在线 | 国产精品久久久久久69 | 婷婷色资源| 精品日韩在线一区 | 国产成人精品av在线观 | 国产精久久久久久妇女av | 激情视频在线高清看 | 狠狠精品| 国产91学生粉嫩喷水 | 久久久久久久久久国产精品 | 久久久免费国产 | 日韩大片在线看 | 在线观看亚洲免费视频 | 狠狠狠狠狠狠干 | 日韩精品免费一区二区 | 欧美日韩免费观看一区=区三区 | 黄色视屏在线免费观看 | 91综合视频在线观看 | 色丁香婷婷 | 欧美怡红院视频 | 最新色站| 天天色天天色天天色 | 奇米777777| 国产精品第52页 | 激情久久一区二区三区 | 日韩色av色资源 | 97在线观看免费视频 | 啪啪激情网 | 国产最新视频在线 | 国产精品美女网站 | 精品麻豆入口免费 | 一本一本久久a久久 | 久草视频在线免费看 | 99re中文字幕 | 国产精品视频一二三 | 99人久久精品视频最新地址 | 久久一区二区三区超碰国产精品 | 日韩在线国产精品 | 五月天网页 | 91看成人| 日韩欧美精品在线观看 | 国内久久看 | 久久久久久久国产精品 | 国产免费嫩草影院 | 91中文字幕网 | 五月婷激情 | 亚洲黄色影院 | 国产美女视频网站 | 日韩在线网 | 又爽又黄又无遮挡网站动态图 | 色婷婷视频在线观看 | 国产一区二区不卡视频 | 久久久久久久久久影视 | 日韩免费 | 成年人在线 | 狠狠色丁香九九婷婷综合五月 | 91免费视频网站在线观看 | 五月天婷婷在线视频 | 成人在线免费av | 日韩视频在线一区 | 国产在线欧美在线 | 国产精品成人自产拍在线观看 | 国产精品永久免费 | 天天av资源 | 国语精品视频 | 亚洲成人av一区 | 日日夜夜天天久久 | 中文字幕在线观看国产 | 色婷婷婷 | av天天色 | 精产嫩模国品一二三区 | 亚洲综合网 | 五月天色中色 | 日韩欧美视频在线观看免费 | 国产亚洲精品久久久久久移动网络 | 天堂网一区二区 | 日韩av中文字幕在线 | 日韩中文字幕免费看 | 久草视频在线看 | av在线播放国产 | 中文字幕 第二区 | 欧美国产日韩一区二区 | 欧美视频www | 成人av网站在线观看 | 五月天婷婷在线视频 | 久久久久久免费毛片精品 | 中文字幕丝袜一区二区 | 国产黄色在线观看 | 99久久精品无码一区二区毛片 | 亚洲一级片 | 免费看黄色小说的网站 | 日韩免费电影一区二区 | 亚洲福利精品 | 深爱婷婷 | 国产成人精品网站 | 国产精品99久久久久久宅男 | 欧美精品一二三 | 91精品福利在线 | 国产高清在线不卡 | 狠色在线| 国产亚洲在线 | 日韩欧美有码在线 | 色婷婷在线播放 | 国产精品18久久久久久久 | 天天操综合 | 福利视频网址 | 黄色中文字幕在线 | 91麻豆精品国产91久久久久久 | 欧美日韩国产区 | 99精品一区 | 日韩免费成人av | 欧美在线aaa | 日韩一级片网址 | 国产精品久久久久永久免费 | 亚洲欧美国产精品18p | 一二三区av | 黄网站免费久久 | 欧美日韩aaaa| 久久久久久久久爱 | 91视频免费视频 | www亚洲视频 | 国产在线观看地址 | 久久永久免费 | 欧美大片在线看免费观看 | 天天操操操操操操 | 国内精品久久久久久久久久久 | 99看视频在线观看 | 久久免费在线观看 | 久草手机视频 | 免费三级骚 | www.av免费 | 激情五月婷婷激情 | 成人毛片100免费观看 | 久久久资源网 | 日日操天天操夜夜操 | 国产福利久久 | 91天天操 | 中文字幕国产精品 | 午夜三级福利 | 超碰97在线资源 | 亚洲一一在线 | 日韩黄色大片在线观看 | 久久视频免费看 | 在线观看精品一区 | 国产精品99蜜臀久久不卡二区 | 日韩成人免费电影 | 91探花国产综合在线精品 | 人人澡人人添人人爽一区二区 | 一级黄色大片 | 久久伦理电影网 | 亚洲三级黄色 | 成人av一区二区在线观看 | 久久深爱网 | 九九热中文字幕 | 成人黄色免费观看 | 91麻豆精品国产自产在线 | 日韩一区二区三区在线观看 | 中文字幕在线人 | 黄色资源在线观看 | 欧美性超爽 | 免费h视频 | 色天天中文| 日韩在线三区 | 久久图 | 91在线看 | 欧美一级电影免费观看 | 欧美综合在线观看 | 欧美日韩精品在线观看 | 久久九九久久精品 | 久久综合免费视频 | 中文字幕第一页av | 亚洲国产影院av久久久久 | 色免费在线| 日韩免费一区二区三区 | 一区二区视频在线播放 | 91夫妻视频| 啪啪精品 | 99亚洲精品视频 | 一级免费看视频 | 韩国av一区二区三区 | 九九热有精品 | 中文字幕视频播放 | 日韩一二区在线 | 国产精品一区在线播放 | 亚洲视频 在线观看 | 国产大片免费久久 | 精品国产欧美一区二区 |