劍指offer
最近在牛客網上刷劍指offer的題目,現將題目和答案(均測試通過)總結如下:
- 二維數組的查找
- 替換空格
- 從尾到頭打印鏈表
- 重建二叉樹
- 用兩個棧實現隊列
- 旋轉數組的最小數字
- 斐波那契數列
- 跳臺階
- 變態跳臺階
- 矩陣覆蓋
- 二進制中1的位數
- 數值的整數次方
- 調整數組順序使奇數位于偶數前面
- 鏈表中倒數第k個結點
- 反轉鏈表
- 合并兩個排序的鏈表
- 樹的子結構
- 二叉樹的鏡像
- 順時針打印矩陣
- 包含min函數的棧
- 棧的壓入、彈出序列
- 從上往下打印二叉樹
- 二叉搜索樹的后序遍歷序列
- 二叉樹中和為某一值的路徑
- 復雜鏈表的復制
- 二叉搜索樹與雙向鏈表
- 字符串的排列
- 數組中出現次數超過一半的數字
- 最小的K個數
- 連續子數組的最大和
- 整數中1出現的次數(從1到n整數中1出現的次數)
- 把數組排成最小的數
- 丑數
1. 二維數組的查找
在一個二維數組中,每一行都按照從左到右遞增的順序排序,每一列都按照從上到下遞增的順序排序。請完成一個函數,輸入這樣的一個二維數組和一個整數,判斷數組中是否含有該整數。
/* 思路: 依次比較右上角的數字;如果該數字大于要查找的數字,則剔除列;如果該數字大于要查找的數字,則剔除行;
復雜度:O(m+n), 行數m,列數n */
class Solution {
public:
bool Find(int target, vector<vector<int> > array) {
bool found=false;
if (array.empty())
return found;
int rows, columns, row, column;
rows = array.size();
columns = array[0].size();
row = 0;
column = columns - 1;
while(row < rows && column >= 0)
{
if(array[row][column] == target)
{
found = true;
break;
}
else if (array[row][column] > target)
-- column;
else
++ row;
}
return found;
}
};
2. 替換空格
請實現一個函數,將一個字符串中的空格替換成“%20”。例如,當字符串為We Are Happy.則經過替換之后的字符串為We%20Are%20Happy。
/* 思路:首先計算原字符串長度,空格個數;然后計算替換之后的長度;設置兩個指針分別指向原,新字符串的尾部,逐個賦值;
復雜度:O(n) */
class Solution {
public:
void replaceSpace(char *str,int length) {
if(str == nullptr || length <=0)
return;
int original_length = 0;
int number_of_space = 0;
int i = 0;
while(str[i] != '\0')
{
++ original_length;
if(str[i] == ' ')
++ number_of_space;
++ i;
}
if (number_of_space <= 0)
return;
int new_length = original_length + 2*number_of_space;
int index_of_original = original_length;
int index_of_new = new_length;
while(index_of_original>=0 && index_of_new>=index_of_original)
{
if(str[index_of_original] == ' ')
{
str[index_of_new--] = '0';
str[index_of_new--] = '2';
str[index_of_new--] = '%';
}
else
{
str[index_of_new--] = str[index_of_original];
}
-- index_of_original;
}
}
};
3. 從尾到頭打印鏈表
輸入一個鏈表,從尾到頭打印鏈表每個節點的值。
/**
* struct ListNode {
* int val;
* struct ListNode *next;
* ListNode(int x) :
* val(x), next(NULL) {
* }
* };
*/
// 思路:借助輔助棧,或者使用遞歸;
class Solution {
public:
vector<int> printListFromTailToHead(ListNode* head) {
vector<int> reverse_list;
stack<int> nodes;
ListNode *p_node = head;
while(p_node != nullptr)
{
nodes.push(p_node->val);
p_node = p_node->next;
}
int tempVal;
while(!nodes.empty())
{
tempVal = nodes.top();
reverse_list.push_back(tempVal);
nodes.pop();
}
return reverse_list;
}
};
4.重建二叉樹
輸入某二叉樹的前序遍歷和中序遍歷的結果,請重建出該二叉樹。假設輸入的前序遍歷和中序遍歷的結果中都不含重復的數字。例如輸入前序遍歷序列{1,2,4,7,3,5,6,8}和中序遍歷序列{4,7,2,1,5,3,8,6},則重建二叉樹并返回。
/**
* Definition for binary tree
* 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.empty() || vin.empty() || pre.size()!=vin.size())
return nullptr;
vector<int> left_pre, right_pre, left_vin, right_vin;
TreeNode *node = new TreeNode(pre[0]);
int left_length = 0;
while(pre[0]!=vin[left_length] && left_length < pre.size())
++ left_length;
for(int i=0; i<left_length; i++)
{
left_pre.push_back(pre[i+1]);
left_vin.push_back(vin[i]);
}
for(int i=left_length+1; i<pre.size(); i++)
{
right_pre.push_back(pre[i]);
right_vin.push_back(vin[i]);
}
node->left = reConstructBinaryTree(left_pre, left_vin);
node->right = reConstructBinaryTree(right_pre, right_vin);
return node;
}
};
5.用兩個棧實現隊列
用兩個棧來實現一個隊列,完成隊列的Push和Pop操作。 隊列中的元素為int類型。
/*思路:stack1:負責壓棧,stack2負責彈棧(如果為空,則將stack1中元素壓入stack2);*/
class Solution
{
public:
void push(int node) {
stack1.push(node);
}
int pop() {
if(stack2.empty())
{
while(!stack1.empty())
{
int val = stack1.top();
stack1.pop();
stack2.push(val);
}
}
int val = stack2.top();
stack2.pop();
return val;
}
private:
stack<int> stack1;
stack<int> stack2;
};
6. 旋轉數組的最小數字
把一個數組最開始的若干個元素搬到數組的末尾,我們稱之為數組的旋轉。
輸入一個非遞減排序的數組的一個旋轉,輸出旋轉數組的最小元素。
例如數組{3,4,5,1,2}為{1,2,3,4,5}的一個旋轉,該數組的最小值為1。
NOTE:給出的所有元素都大于0,若數組大小為0,請返回0。
/*簡單方法*/
class Solution {
public:
int minNumberInRotateArray(vector<int> rotateArray)
{ //數組為空時
if(rotateArray.size() == 0)
return -1;
//前部分數據旋轉
for(int i = 0; i < rotateArray.size() - 1; i++)
{
if (rotateArray[i] > rotateArray[i + 1])
return rotateArray[i + 1];
}
//全部數據旋轉,相當于沒有旋轉,最小數即為第一個數
return rotateArray[0];
}
};
/*思路:二分查找思想*/
class Solution {
public:
int minNumberInRotateArray(vector<int> rotateArray) {
int length = rotateArray.size();
if (length == 0)
return 0;
int left = 0, right = length-1;
int mid;
while(rotateArray[left] >= rotateArray[right])
{
if(left == right - 1)
return rotateArray[right];
mid = (left + right)/2;
if(rotateArray[left] == rotateArray[mid] &&
rotateArray[mid] == rotateArray[right])
{
int min_num = rotateArray[left];
for(int i=left; i < right; i++)
min_num = rotateArray[i]<min_num? rotateArray[i]:min_num;
return min_num;
}
if(rotateArray[left] <= rotateArray[mid])
left = mid;
else
right = mid;
}
return rotateArray[left];
}
};
7.斐波那契數列
大家都知道斐波那契數列,現在要求輸入一個整數n,請你輸出斐波那契數列的第n項。
n<=39
/* 思路: 循環,保存中間結果(遞歸的話,重復計算太多)*/
class Solution {
public:
int Fibonacci(int n) {
if(n<=0)
return 0;
if(n==1)
return 1;
int fib1=1, fib2=0;
int fibn;
for(int i=2; i<=n; i++)
{
fibn = fib1+fib2;
fib2 = fib1;
fib1 = fibn;
}
return fibn;
}
};
8. 跳臺階
一只青蛙一次可以跳上1級臺階,也可以跳上2級。求該青蛙跳上一個n級的臺階總共有多少種跳法。
class Solution {
public:
int jumpFloor(int number) {
if(number == 1)
return 1;
int pre1=1, pre2=1;
int cur;
for(int i=2; i<=number; i++)
{
cur = pre1 + pre2;
pre2 = pre1;
pre1 = cur;
}
return cur;
}
};
9.變態跳臺階
一只青蛙一次可以跳上1級臺階,也可以跳上2級……它也可以跳上n級。求該青蛙跳上一個n級的臺階總共有多少種跳法
思路:其實是隔板問題,假設n個臺階,有n-1個空隙,可以用0~n-1個隔板分割,c(n-1,0)+c(n-1,1)+...+c(n-1,n-1)=2^(n-1),其中c表示組合。 有人用移位1<<--number,這是最快的。
class Solution {
public:
int jumpFloorII(int number) {
int jump_number = 1;
for(int i=0; i<number-1; i++)
jump_number = jump_number * 2;
return jump_number;
}
};
/**********更加簡單的方法**********/
class Solution {
public:
int jumpFloorII(int number) {
return 1<<(--number);
}
};
10. 矩陣覆蓋
我們可以用21的小矩形橫著或者豎著去覆蓋更大的矩形。請問用n個21的小矩形無重疊地覆蓋一個2*n的大矩形,總共有多少種方法?
/* 思路:
第一塊有兩種方式:橫著放和豎著放
橫這放對應為發f(n-2);
豎著放下一步的放方法為f(n-1);
所以總的放的方法為f(n)=f(n-1)+f(n-2);
*/
class Solution {
public:
int rectCover(int number) {
if(number <= 2)
return number;
int pre1 = 2, pre2 = 1;
int cur;
for(int i=2; i<number; i++)
{
cur = pre1 + pre2;
pre2 = pre1;
pre1 = cur;
}
return cur;
}
};
11. 二進制中1的位數
輸入一個整數,輸出該數二進制表示中1的個數。其中負數用補碼表示。
/************ 簡單方法 ************/
class Solution {
public:
int NumberOf1(int n) {
int count = 0;
unsigned int flag = 1;
while(flag)
{
if(n & flag)
++ count;
flag = flag << 1;
}
return count;
}
};
/******* 巧妙方法 *******/
思路:一個整數減去1,在與原整數做與運算,會將最右邊的一個1變成0.
那么二進制中有多少個1,可進行這樣的操作多少次;
class Solution {
public:
int NumberOf1(int n) {
int count = 0;
while(n)
{
++ count;
n = (n-1)&n;
}
return count;
}
};
12. 數值的整數次方
給定一個double類型的浮點數base和int類型的整數exponent。求base的exponent次方。
/*思路 需要考慮以下幾種情況
1) base的正負;
2) base是否等于0;
3) exponent的正負;
4) exponent是否為1;*/
class Solution {
public:
double Power(double base, int exponent) {
if (base>-0.0000001 && base<0.0000001 && exponent<0)
return 0.0;
double result = 1.0;
unsigned int abs_exponent = (unsigned int) exponent;
if(exponent < 0)
abs_exponent = (unsigned int) (-exponent);
/*
for(int i=0; i<abs_exponent; i++)
result = result * base;
*/
//
if(abs_exponent == 0)
return 1.0;
if(abs_exponent == 1)
return base;
result = base;
abs_exponent = abs_exponent >> 1;
while(abs_exponent)
{
result *= result;
abs_exponent = abs_exponent >> 1;
}
if(exponent & 0x1 == 1)
result *= base;
//
if(exponent < 0 && result > 0.0)
result = 1.0 / result;
return result;
}
};
13. 調整數組順序使奇數位于偶數前面
輸入一個整數數組,實現一個函數來調整該數組中數字的順序,使得所有的奇數位于數組的前半部分,所有的偶數位于位于數組的后半部分,并保證奇數和奇數,偶數和偶數之間的相對位置不變。
注:相比劍指offer書要難(要保證相對順序不變)
class Solution {
public:
void reOrderArray(vector<int> &array) {
int length = array.size();
if(length==0 || length==1)
return;
int index_even=0, index_odd;
while(index_even<length)
{
while(index_even<length && !isEven(array[index_even]))
++ index_even;
index_odd = index_even+1;
while(index_odd<length && isEven(array[index_odd]))
++ index_odd;
if(index_odd<length)
{
int temp = array[index_odd];
for(int i=index_odd; i>index_even; i--)
array[i] = array[i-1];
array[index_even] = temp;
}
else
break;
}
}
bool isEven(int number){
if((number & 0x1) == 0)
return true;
return false;
}
};
/*************方法二 申請空間***********/
class Solution {
public:
void reOrderArray(vector<int> &array) {
int length = array.size();
if(length==0 || length ==1)
return;
vector<int> res;
for(int i=0; i<length; i++)
{
if((array[i]&0x1) != 0)
res.push_back(array[i]);
}
for(int i=0; i<length; i++)
{
if((array[i]&0x1) == 0)
res.push_back(array[i]);
}
array = res;
}
};
14. 鏈表中倒數第k個結點
輸入一個鏈表,輸出該鏈表中倒數第k個結點。
/*
struct ListNode {
int val;
struct ListNode *next;
ListNode(int x) :
val(x), next(NULL) {
}
};*/
// 定義快慢指針,快的先走K步;
class Solution {
public:
ListNode* FindKthToTail(ListNode* pListHead, unsigned int k) {
if(pListHead == nullptr || k==0)
return nullptr;
ListNode *pAhead = pListHead;
ListNode *pAfter = pListHead;
for(int i=0; i<k-1; i++)
{
if(pAhead->next != nullptr)
pAhead = pAhead->next;
else
return nullptr;
}
while(pAhead->next != nullptr)
{
pAhead = pAhead->next;
pAfter = pAfter->next;
}
return pAfter;
}
};
15. 反轉鏈表
輸入一個鏈表,反轉鏈表后,輸出鏈表的所有元素。
/*
struct ListNode {
int val;
struct ListNode *next;
ListNode(int x) :
val(x), next(NULL) {
}
};*/
/* 思路:定義三個指針,分別指向當前結點,前一個結點,后一個結點 */
class Solution {
public:
ListNode* ReverseList(ListNode* pHead) {
if(pHead == nullptr)
return nullptr;
if(pHead->next == nullptr)
return pHead;
ListNode *pPreNode=pHead, *pCurNode=pHead->next, *pNextNode;
pPreNode->next = nullptr;
while(pCurNode->next != nullptr)
{
pNextNode = pCurNode->next;
pCurNode->next = pPreNode;
pPreNode = pCurNode;
pCurNode = pNextNode;
}
pCurNode->next = pPreNode;
return pCurNode;
}
};
16.合并兩個排序的鏈表
輸入兩個單調遞增的鏈表,輸出兩個鏈表合成后的鏈表,當然我們需要合成后的鏈表滿足單調不減規則。
/*
struct ListNode {
int val;
struct ListNode *next;
ListNode(int x) :
val(x), next(NULL) {
}
};*/
/*------------------------------方法一 遞歸版本--------------------------*/
class Solution {
public:
ListNode* Merge(ListNode* pHead1, ListNode* pHead2)
{
if(pHead1 == nullptr)
return pHead2;
else if(pHead2 == nullptr)
return pHead1;
ListNode *pMerge;
if(pHead1->val <= pHead2->val)
{
pMerge = pHead1;
pHead1->next = Merge(pHead1->next, pHead2);
}
else
{
pMerge = pHead2;
pHead2->next = Merge(pHead1, pHead2->next);
}
return pMerge;
}
};
17. 樹的子結構
輸入兩棵二叉樹A,B,判斷B是不是A的子結構。(ps:我們約定空樹不是任意一個樹的子結構)
/*
struct TreeNode {
int val;
struct TreeNode *left;
struct TreeNode *right;
TreeNode(int x) :
val(x), left(NULL), right(NULL) {
}
};*/
/*分兩步,判斷根節點是否相等;判斷子結構是否相等*/
class Solution {
public:
bool HasSubtree(TreeNode* pRoot1, TreeNode* pRoot2)
{
bool result = false;
if(pRoot1!=nullptr && pRoot2!=nullptr)
{
if(pRoot1->val == pRoot2->val)
result = DoesTree1HaveTree2(pRoot1, pRoot2);
if(!result)
result = HasSubtree(pRoot1->left, pRoot2);
if(!result)
result = HasSubtree(pRoot1->right, pRoot2);
}
return result;
}
bool DoesTree1HaveTree2(TreeNode *Tree1, TreeNode *Tree2)
{
if(Tree2 == nullptr)
return true;
if(Tree1 == nullptr)
return false;
if(Tree1->val != Tree2->val)
return false;
return DoesTree1HaveTree2(Tree1->left, Tree2->left) &&
DoesTree1HaveTree2(Tree1->right, Tree2->right);
}
};
18. 二叉樹的鏡像
操作給定的二叉樹,將其變換為源二叉樹的鏡像。
/*
struct TreeNode {
int val;
struct TreeNode *left;
struct TreeNode *right;
TreeNode(int x) :
val(x), left(NULL), right(NULL) {
}
};*/
/* 思路:相當于樹的遍歷 */
/*-------------- 遞歸方法 ------------*/
class Solution {
public:
void Mirror(TreeNode *pRoot) {
if(pRoot == nullptr || (pRoot->left==nullptr && pRoot->right==nullptr))
return;
if(pRoot->left != nullptr)
Mirror(pRoot->left);
if(pRoot->right != nullptr)
Mirror(pRoot->right);
TreeNode *temp = pRoot->left;
pRoot->left = pRoot->right;
pRoot->right = temp;
}
};
/*------------- 使用棧 ------------------*/
class Solution {
public:
void Mirror(TreeNode *pRoot) {
if(pRoot == nullptr || (pRoot->left==nullptr && pRoot->right==nullptr))
return;
stack<TreeNode*> stackNodes;
stackNodes.push(pRoot);
while(stackNodes.size() > 0)
{
TreeNode *pNode = stackNodes.top();
stackNodes.pop();
TreeNode *pTemp = pNode->left;
pNode->left = pNode->right;
pNode->right = pTemp;
if(pNode->left != nullptr)
stackNodes.push(pNode->left);
if(pNode->right != nullptr)
stackNodes.push(pNode->right);
}
}
};
19. 順時針打印矩陣
輸入一個矩陣,按照從外向里以順時針的順序依次打印出每一個數字,例如,如果輸入如下矩陣: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 則依次打印出數字1,2,3,4,8,12,16,15,14,13,9,5,6,7,11,10.
class Solution {
public:
vector<int> printMatrix(vector<vector<int> > matrix) {
int rows, columns;
if(matrix.size()>0)
{
rows = matrix.size();
columns = matrix[0].size();
}
vector<int> result;
int startX = 0, endX = columns-1;
int startY = 0, endY = rows-1;
while(startX <= endX && startY<=endY)
{
if(startX<=endX && startY<=endY)
{
for(int i=startX; i<=endX; i++)
result.push_back(matrix[startY][i]);
++ startY;
}
if(startY<=endY && startX<=endX)
{
for(int i=startY; i<=endY; i++)
result.push_back(matrix[i][endX]);
-- endX;
}
if(startX<=endX && startY<=endY)
{
for(int i=endX; i>=startX; i--)
result.push_back(matrix[endY][i]);
-- endY;
}
if(startY<=endY && startX<=endX)
{
for(int i=endY; i>=startY; i--)
result.push_back(matrix[i][startX]);
++ startX;
}
}
return result;
}
};
20. 包含min函數的棧
定義棧的數據結構,請在該類型中實現一個能夠得到棧最小元素的min函數。
/* 借助輔助棧,保存每次壓棧之后的最小值 */
class Solution {
public:
void push(int value) {
if(s_data.empty()){
s_data.push(value);
s_min.push(value);
}
else{
s_min.push(value<s_min.top()?value:s_min.top());
s_data.push(value);
}s
}
void pop() {
s_data.pop();
s_min.pop();
}
int top() {
return s_data.top();
}
int min() {
return s_min.top();
}
private:
stack<int> s_data;
stack<int> s_min;
};
21. 棧的壓入、彈出序列
輸入兩個整數序列,第一個序列表示棧的壓入順序,請判斷第二個序列是否為該棧的彈出順序。假設壓入棧的所有數字均不相等。例如序列1,2,3,4,5是某棧的壓入順序,序列4,5,3,2,1是該壓棧序列對應的一個彈出序列,但4,3,5,1,2就不可能是該壓棧序列的彈出序列。(注意:這兩個序列的長度是相等的)
/* 輔助棧:模擬整個過程 */
class Solution {
public:
bool IsPopOrder(vector<int> pushV,vector<int> popV) {
int length = pushV.size();
int length2 = popV.size();
if(length<=0 || length2<=0 || (length!=length2))
return false;
stack<int> stackV;
int index_push = 0, index_pop=0;
while(index_pop < length)
{
while(stackV.empty() || stackV.top()!=popV[index_pop])
{
if(index_push == length)
break;
stackV.push(pushV[index_push++]);
}
if(stackV.top() != popV[index_pop])
break;
++ index_pop;
stackV.pop();
}
if(stackV.empty() && index_pop==length)
return true;
else
return false;
}
};
22. 從上往下打印二叉樹
從上往下打印出二叉樹的每個節點,同層節點從左至右打印。
/*
struct TreeNode {
int val;
struct TreeNode *left;
struct TreeNode *right;
TreeNode(int x) :
val(x), left(NULL), right(NULL) {
}
};*/
/* 思路:輔助隊列 */
class Solution {
public:
vector<int> PrintFromTopToBottom(TreeNode* root){
vector<int> result;
if(root == nullptr)
return result;
queue<TreeNode *> nodes;
nodes.push(root);
while(!nodes.empty())
{
TreeNode *pNode = nodes.front();
result.push_back(pNode->val);
if(pNode->left != nullptr)
nodes.push(pNode->left);
if(pNode->right != nullptr)
nodes.push(pNode->right);
nodes.pop();
}
return result;
}
};
23. 二叉搜索樹的后序遍歷序列
輸入一個整數數組,判斷該數組是不是某二叉搜索樹的后序遍歷的結果。如果是則輸出Yes,否則輸出No。假設輸入的數組的任意兩個數字都互不相同。
/* 遞歸判斷:左子樹<根節點<右子樹,不滿足則為false,否則為true; */
class Solution {
public:
bool VerifySquenceOfBST(vector<int> sequence) {
if(sequence.size()<=0)
return false;
int start=0, end=sequence.size()-1;
return isLastOrder(sequence, start, end);
}
private:
bool isLastOrder(vector<int> &sequence, int start, int end)
{
if(start > end)
return false;
int root = sequence[end];
int i = start;
for(; i<end; i++)
{
if(sequence[i]>root)
break;
}
int j = i;
for(; j<end; j++)
{
if(sequence[j]<root)
return false;
}
bool left = true;
if(i-1 > start)
left = isLastOrder(sequence, start, i-1);
bool right = true;
if(i < end-1)
right = isLastOrder(sequence, i, end-1);
return(left && right);
}
};
24. 二叉樹中和為某一值的路徑
輸入一顆二叉樹和一個整數,打印出二叉樹中結點值的和為輸入整數的所有路徑。路徑定義為從樹的根結點開始往下一直到葉結點所經過的結點形成一條路徑。
/*
struct TreeNode {
int val;
struct TreeNode *left;
struct TreeNode *right;
TreeNode(int x) :
val(x), left(NULL), right(NULL) {
}
};*/
/* 回溯法,終止條件是為葉子節點,且值相等; */
class Solution {
public:
vector<vector<int> > FindPath(TreeNode* root,int expectNumber) {
vector<vector<int>> pathes;
if(root==nullptr)
return pathes;
vector<int> onePath;
int curSum = 0;
findPath(root, pathes, onePath, expectNumber, curSum);
return pathes;
}
private:
void findPath(TreeNode *pRoot,vector<vector<int>> &pathes, vector<int> onePath, int expectNumber, int &curSum)
{
curSum += pRoot->val;
onePath.push_back(pRoot->val);
bool isLeaf = false;
if(pRoot->left==nullptr && pRoot->right==nullptr)
isLeaf = true;
if(isLeaf && curSum==expectNumber)
{
pathes.push_back(onePath);
}
if(pRoot->left != nullptr)
findPath(pRoot->left, pathes, onePath, expectNumber, curSum);
if(pRoot->right != nullptr)
findPath(pRoot->right, pathes, onePath, expectNumber, curSum);
curSum -=pRoot->val;
onePath.pop_back();
}
};
25. 復雜鏈表的復制
輸入一個復雜鏈表(每個節點中有節點值,以及兩個指針,一個指向下一個節點,另一個特殊指針指向任意一個節點),返回結果為復制后復雜鏈表的head。(注意,輸出結果中請不要返回參數中的節點引用,否則判題程序會直接返回空)
/*
struct RandomListNode {
int label;
struct RandomListNode *next, *random;
RandomListNode(int x) :
label(x), next(NULL), random(NULL) {
}
};
*/
/*分為三步:
1)在舊鏈表中創建新鏈表,此時不處理新鏈表的兄弟節點 ;
2)根據舊鏈表的兄弟節點,初始化新鏈表的兄弟節點;
3)從舊鏈表中拆分得到新鏈表;*/
class Solution {
public:
RandomListNode* Clone(RandomListNode* pHead)
{
if(pHead == nullptr)
return nullptr;
// step.1 Copy Node
RandomListNode *pNode=pHead;
while(pNode!=nullptr)
{
RandomListNode* pCloned = new RandomListNode(pNode->label);
pCloned->next = pNode->next;
// newNode->random = nullptr;
pNode->next = pCloned;
pNode = pCloned->next;
}
// step.2 Copy random
pNode = pHead;
while(pNode!=nullptr)
{
RandomListNode *pCloned = pNode->next;
if(pNode->random != nullptr)
pCloned->random = pNode->random->next;
pNode = pCloned->next;
}
// step.3 Split
pNode = pHead;
RandomListNode *pCloneHead, *pCloneNode;
if(pNode!=nullptr)
{
pCloneHead = pCloneNode = pHead->next;
pNode->next = pCloneNode->next;
pNode = pNode->next;
}
while(pNode != nullptr)
{
pCloneNode->next = pNode->next;
pCloneNode = pCloneNode->next;
pNode->next = pCloneNode->next;
pNode = pNode->next;
}
return pCloneHead;
}
};
26. 二叉搜索樹與雙向鏈表
輸入一棵二叉搜索樹,將該二叉搜索樹轉換成一個排序的雙向鏈表。要求不能創建任何新的結點,只能調整樹中結點指針的指向。
/*
struct TreeNode {
int val;
struct TreeNode *left;
struct TreeNode *right;
TreeNode(int x) :
val(x), left(NULL), right(NULL) {
}
};*/
/*解題思路:遞歸版
1.將左子樹構造成雙鏈表,并返回鏈表頭節點。2.定位至左子樹雙鏈表最后一個節點。3.如果左子樹鏈表不為空的話,將當前root追加到左子樹鏈表。4.將右子樹構造成雙鏈表,并返回鏈表頭節點。5.如果右子樹鏈表不為空的話,將該鏈表追加到root節點之后。6.根據左子樹鏈表是否為空確定返回的節點。*/
class Solution {
public:
TreeNode* Convert(TreeNode* pRootOfTree)
{
if(pRootOfTree==nullptr)
return nullptr;
pRootOfTree = ConvertNode(pRootOfTree);
while(pRootOfTree->left!=nullptr)
pRootOfTree = pRootOfTree->left;
return pRootOfTree;
}
TreeNode* ConvertNode(TreeNode *pRoot)
{
if(pRoot==nullptr)
return nullptr;
if(pRoot->left!=nullptr)
{
TreeNode *left = ConvertNode(pRoot->left);
while(left->right!=nullptr)
left = left->right;
pRoot->left = left;
left->right = pRoot;
}
if(pRoot->right != nullptr)
{
TreeNode *right=ConvertNode(pRoot->right);
while(right->left!=nullptr)
right = right->left;
pRoot->right = right;
right->left = pRoot;
}
return pRoot;
}
};
27. 字符串的排列
輸入一個字符串,按字典序打印出該字符串中字符的所有排列。例如輸入字符串abc,則打印出由字符a,b,c所能排列出來的所有字符串abc,acb,bac,bca,cab和cba。
/*全排列問題*/
class Solution {
public:
vector<string> Permutation(string str) {
vector<string> result;
if(str.size()<=0)
return result;
PermutationCore(result, str, 0);
sort(result.begin(), result.end());
return result;
}
void PermutationCore(vector<string> &result, string str, int begin)
{
if(begin == str.size()-1)
result.push_back(str);
else
{
for(int i=begin; i<str.size(); i++)
{
if(i!=begin && str[i]==str[begin])
continue;
swap(str[i], str[begin]);
PermutationCore(result, str, begin+1);
swap(str[i], str[begin]);
}
}
}
};
28. 數組中出現次數超過一半的數字
數組中有一個數字出現的次數超過數組長度的一半,請找出這個數字。例如輸入一個長度為9的數組{1,2,3,2,2,2,5,4,2}。由于數字2在數組中出現了5次,超過數組長度的一半,因此輸出2。如果不存在則輸出0。
/*一個思路是基于快排中partition(會修改數組中的值);
還有就是:定義一個times記錄當前牟數字出現的次數,如果小于0則替換;
復雜度都是O(n)
*/
class Solution {
public:
int MoreThanHalfNum_Solution(vector<int> numbers) {
if(numbers.size() <= 0)
return 0;
int result = numbers[0];
int times = 1;
for(int i=0; i<numbers.size(); i++)
{
if(times==0)
{
result = numbers[i];
times = 1;
}
else if(times>0 && result==numbers[i])
++ times;
else
-- times;
}
if(!isMoreThanHalf(numbers, result))
return 0;
return result;
}
private:
bool isMoreThanHalf(vector<int> numbers, int result)
{
int times = 0;
for(int i=0; i<numbers.size(); i++)
if(numbers[i] == result)
++ times;
if(2*times <= numbers.size())
return false;
return true;
}
};
29. 最小的K個數
輸入n個整數,找出其中最小的K個數。例如輸入4,5,1,6,2,7,3,8這8個數字,則最小的4個數字是1,2,3,4,。
/*復雜度為O(n logn)*/
class Solution {
public:
vector<int> GetLeastNumbers_Solution(vector<int> input, int k) {
vector<int> result;
if(input.size()<k || k<1)
return result;
sort(input.begin(),input.end());
for(int i=0; i<k; i++)
result.push_back(input[i]);
return result;
}
};
/*復雜度為O(nlogk), 基于紅黑樹*/
class Solution {
public:
vector<int> GetLeastNumbers_Solution(vector<int> input, int k) {
if(input.size()<k || k<0)
return vector<int> ();
multiset<int, greater<int>> leastNumbers;
vector<int>::const_iterator iter=input.begin();
for(; iter!=input.end(); iter++)
{
if(leastNumbers.size() < k)
leastNumbers.insert(*iter);
else
{
multiset<int, greater<int>>::iterator iterGreatest=leastNumbers.begin();
if(*iter < *iterGreatest)
{
leastNumbers.erase(*iterGreatest);
leastNumbers.insert(*iter);
}
}
}
return vector<int>(leastNumbers.begin(), leastNumbers.end());
}
};
30. 連續子數組的最大和
HZ偶爾會拿些專業問題來忽悠那些非計算機專業的同學。今天測試組開完會后,他又發話了:在古老的一維模式識別中,常常需要計算連續子向量的最大和,當向量全為正數的時候,問題很好解決。但是,如果向量中包含負數,是否應該包含某個負數,并期望旁邊的正數會彌補它呢?例如:{6,-3,-2,7,-15,1,2,2},連續子向量的最大和為8(從第0個開始,到第3個為止)。你會不會被他忽悠住?(子向量的長度至少是1)
/* 思路:動態規劃復雜度為O(n), 首先定義一個值保存當前最大值;
如果當前和為負數,直接舍棄;如果不為負數,則累加;得到 當前和 與 當前最大值 比較*/
class Solution {
public:
int FindGreatestSumOfSubArray(vector<int> array) {
if(array.size() < 1)
return 0;
int curSum = array[0];
int greatestSum = array[0];
for(int i=1; i<array.size(); i++)
{
if(curSum < 0)
curSum = array[i];
else
curSum += array[i];
if(greatestSum < curSum)
greatestSum = curSum;
}
return greatestSum;
}
};
31. 整數中1出現的次數(從1到n整數中1出現的次數)
求出113的整數中1出現的次數,并算出1001300的整數中1出現的次數?為此他特別數了一下1~13中包含1的數字有1、10、11、12、13因此共出現6次,但是對于后面問題他就沒轍了。ACMer希望你們幫幫他,并把問題更加普遍化,可以很快的求出任意非負整數區間中1出現的次數。
/*思路:窮舉*/
class Solution {
public:
int NumberOf1Between1AndN_Solution(int n)
{
if(n < 1)
return 0;
int count=0;
for(int i=1; i<=n; i++)
{
count += NumberOfN(i);
}
return count;
}
int NumberOfN(int n)
{
int count=0;
while(n)
{
if(n%10 == 1)
count += 1;
n = n/10;
}
return count;
}
};
32. 把數組排成最小的數
輸入一個正整數數組,把數組里所有數字拼接起來排成一個數,打印能拼接出的所有數字中最小的一個。例如輸入數組{3,32,321},則打印出這三個數字能排成的最小數字為321323。
/*思路:通過字符串解決大數問題,然后通過自定義的字符串比較規則,對字符串排序*/
class Solution {
public:
string PrintMinNumber(vector<int> numbers) {
if(numbers.size()<1)
return string();
string result;
vector<string> numberString;
for(int i=0; i<numbers.size(); i++)
{
stringstream ss;
ss<<numbers[i];
string s = ss.str();
numberString.push_back(s);
}
sort(numberString.begin(), numberString.end(), Compare);
for(int i=0; i<numberString.size(); i++)
result.append(numberString[i]);
return result;
}
static bool Compare(const string &str1, const string &str2)
{
string s1 = str1+str2;
string s2 = str2+str1;
return s1<s2;
}
};
33. 丑數
把只包含因子2、3和5的數稱作丑數(Ugly Number)。例如6、8都是丑數,但14不是,因為它包含因子7。 習慣上我們把1當做是第一個丑數。求按從小到大的順序的第N個丑數。
/* 思路:創建數組保存已經找到的丑數,空間換時間; */
class Solution {
public:
int GetUglyNumber_Solution(int index) {
if(index <= 0)
return 0;
vector<int> res(index);
res[0] = 1;
int t2=0, t3=0, t5=0;
for(int i=1; i<index; i++)
{
res[i] = min(2*res[t2], min(3*res[t3], 5*res[t5]));
while(res[i] >= 2*res[t2]) ++t2;
while(res[i] >= 3*res[t3]) ++t3;
while(res[i] >= 5*res[t5]) ++t5;
}
return res[index-1];
}
};