數組中重復的數字
二維數組中查找
字符串 替換空格
二叉樹的編碼和解碼
從尾到頭打印鏈表
重建二叉樹
二叉樹的下一個節點
2個棧實現隊列
斐波那契數列
旋轉數字
矩陣中的路徑
機器人的運動范圍
剪繩子
二進制表示中1的個數
數值的整數次方
打印1到最大的n位數
在O(1)時間刪除鏈表結點
刪除鏈表中重復的結點
正則表達式匹配
表示數值的字符串
調整數組順序使奇數位於偶數前面
鏈表中倒數第k個結點
一個鏈表中包含環,如何找出環的入口結點
反轉鏈表
合並兩個排序鏈表
輸入兩棵二叉樹A,B,判斷B是不是A的子結構(注意:是子結構,不是子樹)
操作給定的二叉樹,將其變換為源二叉樹的鏡像
判斷二叉樹是不是對稱二叉樹
順時針打印矩陣
棧的壓入、彈出序列
從上往下打印二叉樹
Z打印二叉樹
二叉樹搜索的后序遍歷序列
二叉樹中和為某一值的路徑
復雜鏈表的復制
二叉搜索樹與雙向鏈表
序列化二叉樹
字符串的排列
數字出現的次數超過數組長度的一半
最小的k個數
數據流中的中位數
連續子數組的最大和
整數中1出現的次數
數字序列中某一位的數字
把數組排成最小的數
把數字翻譯成字符串
最大價值的禮物
最長不含重復字符的子字符串
丑數
第一次只出現一次的字符的索引
字符流中第一個不重復的字符【不是求索引】
數組中的逆序對
兩個鏈表的第一個公共結點
數字在排序數組中出現的次數
0-n-1中缺失的數字
二叉搜索樹中第K小的元素
二叉樹的深度
判斷是否為平衡二叉樹
數組中只出現一次的兩個數字
數組中只出現一次的兩個數字2
和為S的兩個數
和為S的連續正數序列
翻轉字符串 leetcode(151)
左旋轉字符串
滑動窗口最大值
隊列最大值
n個骰子的點數
撲克牌中的順子
圓圈中最后剩下的數字
股票的最大利潤
求1+2+3…+n
求兩個整數之和
構建乘積數組
最低公共節點
數組中重復的數字
/* 第三題:數組中重復的數字 [2 3 1 0 2 5 3] 為 2或者3 在一個長度為n的數組里的所有數字都在0到n-1的范圍內。 數組中某些數字是重復的,但不知道有幾個數字是重復的。也不知道每個數字重復幾次。請找出數組中任意一個重復的數字。 例如,如果輸入長度為7的數組{2,3,1,0,2,5,3},那么對應的輸出是第一個重復的數字2 */ class class_3{ bool duplicate(int numbers[],int length,int *dup) { if(numbers==nullptr||length<=0){ return false; } for (int i=0; i<length; i++) { if(numbers[i]>=length||numbers[i]<0){ return false; } } for (int i=0; i<length; i++) { while (i!=numbers[i]) { if(numbers[i]==numbers[numbers[i]]){ *dup=numbers[i]; return true; } int temp=numbers[i]; numbers[i]=numbers[temp]; numbers[temp]=temp; } } return false; } };
二維數組中查找
/* 第四題:二維數組中查找 在一個二維數組中,每一行都按照從左到右遞增的順序排序,每一列都按照從上到下遞增的順序排序。 請完成一個函數,輸入這樣的一個二維數組和一個整數,判斷數組中是否含有該整數。 */ class class_2{ bool FindErWEi(vector<vector<int>>&matrix,int target) { bool found=false; int row=0; int column=(int)matrix[0].size()-1; while (row<matrix.size()&&column>=0) { if(matrix[row][column]==target){ found=true; break; } else if(matrix[row][column]<target){ row++; } else{ column--; } } return found; } };
字符串 替換空格
/* 第五題:字符串 替換空格 請實現一個函數,將一個字符串中的每個空格替換成“%20”。 例如,當字符串為We Are Happy.則經過替換之后的字符串為We%20Are%20Happy。 注:"a"和’a’的區別,前者是字符串,后者是字符。 思路:從后往前復制 */ class class_5{ void ReplaceBlank(char string[],int length){ if(string==NULL||length<=0) { return; } int blankLen=0; int originLen=0; int i=0; while (string[i]!='\0') { originLen++; if(*string==' '){ blankLen++; } i++; } int newLength=blankLen*2+originLen; if(newLength>length){ return; } int indexOfOrigin=originLen; int indexOfNew=newLength; //indexOfNew和indexOfOrigin相等就可以結束拷貝了,不加這個條件也是可以的 while (indexOfOrigin>=0&&indexOfNew>indexOfOrigin) { if(string[indexOfOrigin]==' '){ string[indexOfNew--]='0'; string[indexOfNew--]='2'; string[indexOfNew--]='%'; } else{ string[indexOfNew--]=string[indexOfOrigin]; } indexOfOrigin--; } } };
二叉樹的編碼和解碼
/* 第六題: 二叉樹的編碼和解碼 */ class class_6{ //二叉樹編解碼 typedef struct TreeNode{ string val; TreeNode *left; TreeNode *right; TreeNode(string x):val(x),left(NULL),right(NULL){} } TreeNode; //二叉樹的編碼 void BianMa(TreeNode *root,string &ss){ if(root==NULL){ ss+="#_"; return; } string value=root->val; ss+=value+"_"; BianMa(root->left, ss); BianMa(root->right, ss); } //創建新的節點(忽略釋放) TreeNode *getNewNode(string s){ TreeNode *node=new TreeNode(s); return node; } //遞歸前序插入節點 void insertNode(TreeNode* &root,vector<string>&ss,int &i){ if(i==ss.size()){ return; } string node=ss[i]; if(node=="#"){ root=NULL; } else{ if(root==NULL){ root= getNewNode(node); } else{ root->val=node; } i=i+1; insertNode(root->left, ss, i); i=i+1; insertNode(root->right, ss, i); } } //二叉樹解碼 void jiema(TreeNode *root,string &ss){ vector<string>sss; string temp; for (int i=0; i<ss.length(); i++) { if(ss[i]=='_'){ sss.push_back(temp); temp=""; } else{ temp+=ss[i]; } } int i=0; insertNode(root, sss, i); } };
從尾到頭打印鏈表
/* 第七題:從尾到頭打印鏈表 方法1:用棧 方法2:遞歸 */ class class_7{ struct Node{ Node * next; int val; }; //1->2->3 void PrintListReversingly(Node *head){ if(head){ if(head->next){ PrintListReversingly(head->next); } printf("%d ",head->val); } } };
重建二叉樹
/* 重建二叉樹 輸入某二叉樹的前序遍歷和中序遍歷的結果,請重建出該二叉樹。假設輸入的前序遍歷和中序遍歷的結果中都不含重復的數字。 例如輸入前序遍歷序列{1,2,4,7,3,5,6,8}和中序遍歷序列{4,7,2,1,5,3,8,6},則重建二叉樹並返回。 */ class class_7_2{ struct BinaryTreeNode{ BinaryTreeNode *m_pLeft; BinaryTreeNode *m_pRight; int m_nValue; }; BinaryTreeNode *Construct(int *preorder,int * inorder,int length){ if(preorder==nullptr||inorder==nullptr||length<=0){ return nullptr; } return ConstructCore(preorder, preorder+length-1, inorder, inorder+length-1); } BinaryTreeNode * ConstructCore(int *startPreorder,int *endPreorder,int *startInorder,int *endInorder){ int rootValue=startInorder[0]; BinaryTreeNode *root=new BinaryTreeNode(); root->m_nValue=rootValue; root->m_pLeft=NULL; root->m_pRight=NULL; //邊界條件 左邊或者右邊只剩下一個元素 if(startPreorder==endPreorder){ if(startInorder==endInorder){ if(*startPreorder==*startInorder){ return root; } else{ return NULL;//無效的輸入 } } } //前序遍歷序列{1,2,4,7,3,5,6,8}和中序遍歷序列{4,7,2,1,5,3,8,6} //中序遍歷找到跟節點 int *rootInorder=startInorder; while (rootInorder<endInorder&&*rootInorder!=rootValue) { ++rootInorder; } if(rootInorder==endInorder&&*rootInorder!=rootValue){ return NULL;//無效輸入. 沒找到 } //找出來的 *rootInorder =1 //划分左右子樹,然后遞歸 int leftLength=(int)(rootInorder-startInorder); int *leftPreorderEnd=startPreorder+leftLength; //存在左子樹 if(leftLength>0){ root->m_pLeft=ConstructCore(startPreorder+1, leftPreorderEnd, startInorder, rootInorder-1); } //看看是否存在右子樹 //比較左子樹的長度和總長度是否一樣就可以 if(leftLength<endPreorder-startPreorder){ root->m_pRight=ConstructCore(leftPreorderEnd+1, endPreorder, rootInorder+1, endInorder); } return root; } };
二叉樹的下一個節點
/* 第八題:二叉樹的下一個節點 給定一個二叉樹和其中的一個結點,請找出中序遍歷順序的下一個結點並且返回。 注意,樹中的結點不僅包含左右子結點,同時包含指向父結點的指針。 分三種情況: 1 節點有右子樹,那么下一個節點就是右子樹中最左的節點 2 節點沒有右子樹,但是是父節點的左子樹,下一個就是父親節點 3 節點沒有右子樹,並且是父親節點的右節點,下一個節點就是,從當前節點一直找他的父節點,直到找到的某個父節點是 他 父親節點的左子樹,那么 某個父節點的父親節點,就是下一個節點 */ class class_8{ struct BinaryTreeNode { int m_nValue; BinaryTreeNode* m_pLeft; BinaryTreeNode* m_pRight; BinaryTreeNode* m_pParent; }; BinaryTreeNode *GetNext(BinaryTreeNode*node){ if(node==nullptr){ return nullptr; } BinaryTreeNode *pNext=nullptr; if(node->m_pRight){//情況一 BinaryTreeNode *pRight=node->m_pRight; while (pRight->m_pLeft) { pRight=pRight->m_pLeft; } pNext=pRight; } //沒有右子樹 else if(node->m_pParent){ //父節點是自己的左子樹 BinaryTreeNode *pCurrent=node; BinaryTreeNode *pParent=node->m_pParent; if(pParent->m_pLeft==pCurrent){ pNext=pParent; } else{ //一直找,直到找到左子樹的情況 while (pParent&&pCurrent==pParent->m_pRight) { pCurrent=pParent; pParent=pParent->m_pParent; } pNext=pParent; } } return pNext; } };
2個棧實現隊列
/* 第九題:2個棧實現隊列 */ /* 方法一:臨時棧方式,時間浪費在 push */ class class_9{ public: class_9(){} void push(int x){ std::stack<int>temp_stack; while (!_data.empty()) { temp_stack.push(_data.top()); _data.pop(); } temp_stack.push(x); while (!temp_stack.empty()) { _data.push(temp_stack.top()); temp_stack.pop(); } } int pop(){ int x=_data.top(); _data.pop(); return x; } int peek(){ return _data.top(); } bool empty(){ return _data.empty(); } private: std::stack<int>_data; }; /* 方法二:雙棧法 */ class class_9_2 { public: class_9_2(){} void push(int x){ _input.push(x); } int pop(){ adjust(); int x = _output.top(); _output.pop(); return x; } int peek(){ adjust(); return _output.top(); } bool empty(){ return _input.empty() && _output.empty(); } private: void adjust(){ if(!_output.empty()){ return; } while (!_input.empty()) { _output.push(_input.top()); _input.pop(); } } stack<int>_input; stack<int>_output; };
斐波那契數列
/* 第十題: 斐波那契數列 方法一:采用遞歸 方法二:動態規划 */ class class_10 { int Mehotd1(int n){ if(n==1||n==2){ return n; } return Mehotd1(n-1)+Mehotd1(n-2); } int Method2(int n){ vector<int>dp(n+3,0); dp[1]=1; dp[2]=2; for (int i=3; i<=n; i++) { dp[i]=dp[i-1]+dp[i-2]; } return dp[n]; } };
旋轉數字
/* 第十一題 旋轉數字 把一個數組最開始的若干個元素搬到數組的末尾,我們稱之為數組的旋轉。 輸入一個遞增排序的數組的一個旋轉,輸出旋轉數組的最小元素。 例如數組{3,4,5,1,2}為{1,2,3,4,5}的一個旋轉,該數組的最小值為1。 NOTE:給出的所有元素都大於0,若數組大小為0,請返回0。 1 5 7 10 15 22 30=> 7 10 15 22 30 1 5 此解法可以處理元素相同的情況,比如 [3,3,3,1] */ class class_11{ int minInOrder(vector<int>&nums,int begin,int end){ int result = nums[begin]; for (int i=begin+1; i<=end; i++) { if(result>nums[i]){ result = nums[i]; } } return result; } int getXuanZhuanMin(vector<int>&nums){ /* 考慮特殊情況 */ if(nums.size()==0){ return 0; } if(nums.size()==1){ return nums[0]; } if(nums[0]<nums[nums.size()-1]){ return nums[0]; } //3331 int begin=0; int end=(int)nums.size()-1; while (begin<=end) { int mid=(begin+end)/2; if(end-begin==1){ mid=end; return nums[mid]; } //特殊情況 //如果下標為 begin,end,mid指向的三個數字相等,只能順序查找 if(nums[begin]==nums[end]&&nums[begin]==nums[mid]){ return minInOrder(nums, begin, end); } //遞增數組在前,旋轉數組在后,最小值在后面 if(nums[begin]<=nums[mid]){ //7 10 15 22 30 1 5 begin=mid; } //遞增數組在后,旋轉數組在前,最小值在前面 else if(nums[begin]>=nums[mid]){ //22 30 1 5 7 10 15 end=mid;// } } return 0; } };
矩陣中的路徑
/* 第12題 矩陣中的路徑 請設計一個函數,用來判斷在一個矩陣中是否存在一條包含某字符串所有字符的路徑。路徑可以從矩陣中的任意一個格子開始,每一步可以在矩陣中向左,向右,向上,向下移動一個格子。如果一條路徑經過了矩陣中的某一個格子,則之后不能再次進入這個格子。 例如 a b c e s f c s a d e e 這樣的3 X 4 矩陣中包含一條字符串"bcced"的路徑,但是矩陣中不包含"abcb"路徑,因為字符串的第一個字符b占據了矩陣中的第一行第二個格子之后,路徑不能再次進入該格子。 思路: 回溯法 */ class class_12{ bool hasPathCore(const char *matrix,int rows,int cols,int row,int col,const char *str,int &pathLength,bool *visited){ if(str[pathLength]=='\0'){ return true; } bool hasPath=false; if(row >= 0 && row < rows && col >= 0 && col < cols && matrix[row * cols + col] == str[pathLength] && !visited[row * cols + col]){ ++pathLength; visited[row*cols+col]=true; //循環遍歷四個鄰居 int rowN[]={0,0,-1,1}; int colN[]={-1,1,0,0}; for (int i=0; i<4; i++) { hasPath= hasPathCore(matrix, rows, cols, row+rowN[i], col+colN[i], str, pathLength, visited); if(hasPath==true){ return hasPath; } } if(!hasPath){//四個角都沒找到,回溯 pathLength--; visited[row * cols + col]=false; } } return hasPath; } bool hasPath(const char * matrix,int rows,int cols,const char * str){ if(matrix==NULL||rows<1||cols<1||str==nullptr){ return false; } bool *visited = new bool[rows*cols]; memset(visited,0,rows*cols); int pathLength=0; for (int row=0; row<rows; row++) { for (int col=0; col<cols; col++) { if(hasPathCore(matrix, rows, cols, row,col,str,pathLength,visited)){ return true; } } } delete[] visited; return false; } };
機器人的運動范圍
/* 第十三題 機器人的運動范圍 地上有一個m行和n列的方格。一個機器人從坐標0,0的格子開始移動,每一次只能向左,右,上,下四個方向移動一格,但是不能進入行坐標和列坐標的數位之和大於k的格子。 例如,當k為18時,機器人能夠進入方格(35,37),因為3+5+3+7 = 18。但是,它不能進入方格(35,38),因為3+5+3+8 = 19。請問該機器人能夠達到多少個格子? //test movingCount(5, 10, 10) 結果為21 */ class class_13{ //入口方法 public: int movingCount(int threshold,int rows,int cols){ if(threshold < 0 || rows <= 0 || cols <= 0) return 0; bool *visited = new bool[rows * cols]; for(int i = 0; i < rows * cols; ++i) visited[i] = false; int count=0; movingCountCore(threshold,rows,cols,0,0,visited,count); delete []visited; return count; } private: void movingCountCore(int threshold, int rows, int cols, int row, int col, bool* visited,int &count){ if(check(threshold, rows, cols, row, col, visited)){ count++; visited[row * cols + col] = true; //循環遍歷四個鄰居 int rowN[]={0,0,-1,1}; int colN[]={-1,1,0,0}; for (int i=0; i<4; i++) { movingCountCore(threshold, rows, cols, row+rowN[i], col+colN[i], visited,count); } } } bool check(int threshold, int rows, int cols, int row, int col, bool* visited) { if(row >= 0 && row < rows && col >= 0 && col < cols && getDigitSum(row) + getDigitSum(col) <= threshold && !visited[row* cols + col]) return true; return false; } int getDigitSum(int number) { int sum = 0; while(number > 0) { sum += number % 10; number /= 10; } return sum; } };
剪繩子
/* 第14題 剪繩子 題目一:給你一根長度為n的繩子,請把繩子剪成m段 (m和n都是整數,n>1並且m>1)每段繩子的長度記為k[0],k[1],...,k[m] .請問k[0]*k[1]*...*k[m]可能的最大乘積是多少? 例如,當繩子的長度為8時,我們把它剪成長度分別為2,3,3的三段,此時得到的最大乘積是18. */ /* 方法一:動態規划 方法二:貪心(需要數學推算,略) 4個條件: 動態規划原理: 1.確認原問題與子問題: 求長度為n的繩子的最優解,子問題為求n-1,n-2的最優解 2.確認狀態: 第i個狀態,就是長度為i的繩子的最優解 3.確認邊界狀態的值: f(0)=0 f(1)=0 f(2)=1 f(3)=2 4.確定狀態轉移方程: 將求第i個狀態的值轉移為求第i-1個狀態 f(n)=max(f(i)*f(n-i)) 剪長度為i的最優解乘以 長度為 n-i的最優解 */ class class_14 { int maxProductAfterCutting(int length) { if(length<2){ return 0; } if(length==2){ return 1; } if(length==3){ return 2; } vector<int>products(length+1); products[0]=0; products[1]=1; products[2]=2; products[3]=3; int max=0; for (int i=4; i<=length; i++) { max=0; for (int j=1; j<=i/2; j++) { int product=products[j]*products[i-j]; if(max<product){ max=product; } products[i]=max; } } max=products[length]; return max; } };
二進制表示中1的個數
/* 第15題 二進制表示中1的個數 輸入一個整數,輸出該數二進制表示中1的個數。其中負數用補碼表示。 */ class class_15 { //常規方法 //flag左移,比如flag是32位,移動32位最后flag=0,結束循環 int NumberOf2(int n){ int count=0; unsigned int flag = 1; while (flag) { if(n&flag){ count++; } flag=flag<<1; } return count; } //巧妙方法 /* 技巧:把一個整數減去1 之后再和原來的整數做位與運算,得到的結果相當於把整數的二進制表示中d最右邊的1變為0 */ int NumberOf3(int n){ int count=0; while (n) { ++count; n=(n-1)&n;//循環幾次說明有幾個1 } return count; } };
數值的整數次方
/* 第16題 數值的整數次方 給定一個double類型的浮點數base和int類型的整數exponent。求base的exponent次方。 */ class class_16{ public: //求平方 double Power(double base,int exponent){ g_InvalidInput = false; if(equal(base, 0.0)&& exponent < 0){ g_InvalidInput = true; return 0.0; } unsigned int absExponent = (unsigned int) (exponent); if(exponent<0){ absExponent = (unsigned int)(-exponent); } double result = PowerWithUnsignedExponent2(base, absExponent); if (exponent < 0)//負指數 result = 1.0 / result; return result; } bool g_InvalidInput = false; //判斷是否相等 bool equal(double num1, double num2) { if ((num1 - num2 > -0.0000001) && (num1 - num2 < 0.0000001)) return true; else return false; } //遞歸方法 double PowerWithUnsignedExponent2(double base, unsigned int exponent){ if (exponent == 0) return 1; if (exponent == 1) return base; double result = PowerWithUnsignedExponent2( base, exponent>>1);//相當於除以2 result *= result; if ((exponent & 0x1) == 1) result *= base; return result; } //普通方法 double PowerWithUnsignedExponent(double base, unsigned int exponent){ double result = 1.0; for (int i = 1; i <= exponent; ++i) result *= base; return result; } };
打印1到最大的n位數
/* // 面試題17:打印1到最大的n位數 // 題目:輸入數字n,按順序打印出從1最大的n位十進制數。比如輸入3,則 // 打印出1、2、3一直到最大的3位數即999。 要點 3位數所有的其實是1-9 的3個數的全排列 最大為999,最小為000,打印的時候,前面為0 的不打印 */ class class_17{ void PrintToMaxofNDigits_2(int n){ if(n<=0){ return; } char * number = new char[n+1]; number[n]='\0'; for (int i=0; i<10; i++) { number[0] = i+'0'; Print1ToMaxOfNDigitsRecursively(number,n,0); } delete [] number; } void Print1ToMaxOfNDigitsRecursively(char* number, int length, int index){ if(index>=length-1){//到了最低一位 //打印 PrintNumber(number); return; } for (int i=0; i<10; i++) { number[index+1]=i+'0'; Print1ToMaxOfNDigitsRecursively(number,length,index+1); } } // 字符串number表示一個數字,數字有若干個0開頭 // 打印出這個數字,並忽略開頭的0 void PrintNumber(char* number){ bool isBeginning0 = true; int nLength = (int)strlen(number); for (int i=0; i<nLength; i++) { if (isBeginning0 && number[i] != '0') isBeginning0 = false; if(!isBeginning0){ printf("%c", number[i]); } } printf("\t"); } };
在O(1)時間刪除鏈表結點
// 面試題18(一):在O(1)時間刪除鏈表結點 // 題目:給定單向鏈表的頭指針和一個結點指針,定義一個函數在O(1)時間刪除該 // 結點。 class class_18{ void DeleteNode(ListNode** pListHead, ListNode* pToBeDeleted) { if(!pListHead || !pToBeDeleted) return; // 要刪除的結點不是尾結點 //思路很巧妙,改變值來達到 刪除的效果 if(pToBeDeleted->m_pNext != nullptr) { ListNode* pNext = pToBeDeleted->m_pNext; pToBeDeleted->m_nValue = pNext->m_nValue; pToBeDeleted->m_pNext = pNext->m_pNext; delete pNext; pNext = nullptr; } // 鏈表只有一個結點,刪除頭結點(也是尾結點) else if(*pListHead == pToBeDeleted) { delete pToBeDeleted; pToBeDeleted = nullptr; *pListHead = nullptr; } // 鏈表中有多個結點,刪除尾結點 else { ListNode* pNode = *pListHead; while(pNode->m_pNext != pToBeDeleted) { pNode = pNode->m_pNext; } pNode->m_pNext = nullptr; delete pToBeDeleted; pToBeDeleted = nullptr; } } };
刪除鏈表中重復的結點
/* // 面試題18(二):刪除鏈表中重復的結點 // 題目:在一個排序的鏈表中,如何刪除重復的結點? 技巧:利用g一個 前置節點輔助會更簡單 */ class class_18_2{ struct Node{ int value; Node *next; }; Node *deleteDuplication(Node *head){ if(head==NULL) return NULL; Node *preHead = new Node; preHead->next = head; Node *p=head; Node *last=preHead; while (p&&p->next) { if(p->value==p->next->value){ int value=p->value; while (p && p->value==value) { p=p->next; } last->next = p; } else{ last=p; p=p->next; } } return preHead->next; } };
正則表達式匹配
/* //第19題 正則表達式匹配 // 題目:請實現一個函數用來匹配包含'.'和'*'的正則表達式。模式中的字符'.' // 表示任意一個字符,而'*'表示它前面的字符可以出現任意次(含0次)。在本題 // 中,匹配是指字符串的所有字符匹配整個模式。例如,字符串"aaa"與模式"a.a" // 和"ab*ac*a"匹配,但與"aa.a"及"ab*a"均不匹配。 */ class class_19{ bool match(const char* str, const char* pattern) { if(str == nullptr || pattern == nullptr) return false; return matchCore2(str, pattern); } //aa a*ac*a //ba a*bc*a // "cb", "c*b" bool matchCore2( const char* str, const char* pattern){ //結束條件為 都指向字符串末尾,然后返回true if(*str=='\0' && *pattern=='\0') return true; //結束條件為字符串沒有結束,模式指向尾部了(字符串結束,模式沒有結束,不一定不匹配,比如ab abc*d*) if(*str!='\0' && *pattern=='\0') return false; if(*(pattern+1)=='*'){ //如果*前面的字符和字符串匹配,那么有3種方式可以選擇 if(*str==*pattern || (*pattern=='.' && *str!='\0')){ //1 *前面的字符可以在字符串中出現任意次,字符串后移,看看下一個是不是仍然匹配 bool result = matchCore2(str+1,pattern); //2 假設*前的字符和本次字符串指向的字符不匹配(略過本次的*),模式字符串后移2 bool result2 = matchCore2(str,pattern+2); // 假設匹配上了,那么字符串后移1,模式后移2,繼續比較下一個 //"aaaaaaaaaaaaab" "a*a*a*a*a*a*a*a*a*a*a*a*b" // ab a*b //超時了 // bool result3 = matchCore2(str+1, pattern+2); // //3個條件滿足一個就可以 // return result || result2 || result3 ; //3個條件滿足一個就可以 return result || result2 ; } else{ //如果*前的和字符串不匹配,那么就理解為*前的字符串出現0次,也是符合條件的,然后模式字符串后移2 bool result = matchCore2(str,pattern+2); return result; } } if(*str==*pattern || (*pattern=='.' && *str!='\0')){ bool result = matchCore2(str+1,pattern+1); return result; } return false; } void Test(const char* testName, const char* string, const char* pattern, bool expected) { if(testName != nullptr) printf("%s begins: ", testName); if(match(string, pattern) == expected) printf("Passed.\n"); else printf("FAILED.\n"); } };
表示數值的字符串
/* 題目20: 表示數值的字符串 請實現一個函數用來判斷字符串是否表示數值(包括整數和小數)。 例如,字符串"+100","5e2","-123","3.1416"和"-1E-16"都表示數值。但是"12e","1a3.14","1.2.3","+-5"和"12e+4.3"都不是。 */ class class_20{ // 數字的格式可以用A[.[B]][e|EC]或者.B[e|EC]表示,其中A和C都是 // 整數(可以有正負號,也可以沒有),而B是一個無符號整數 bool isNumeric(const char* str) { if(str == nullptr) return false; bool numeric = scanInteger(&str); // 如果出現'.',接下來是數字的小數部分 if(*str == '.') { ++str; // 下面一行代碼用||的原因: // 1. 小數可以沒有整數部分,例如.123等於0.123; // 2. 小數點后面可以沒有數字,例如233.等於233.0; // 3. 當然小數點前面和后面可以有數字,例如233.666 numeric = scanUnsignedInteger(&str) || numeric; } // 如果出現'e'或者'E',接下來跟着的是數字的指數部分 if(*str == 'e' || *str == 'E') { ++str; // 下面一行代碼用&&的原因: // 1. 當e或E前面沒有數字時,整個字符串不能表示數字,例如.e1、e1; // 2. 當e或E后面沒有整數時,整個字符串不能表示數字,例如12e、12e+5.4 numeric = numeric && scanInteger(&str); } return numeric && *str == '\0'; } bool scanUnsignedInteger(const char** str) { const char* before = *str; while(**str != '\0' && **str >= '0' && **str <= '9') ++(*str); // 當str中存在若干0-9的數字時,返回true return *str > before; } // 整數的格式可以用[+|-]B表示, 其中B為無符號整數 bool scanInteger(const char** str) { if(**str == '+' || **str == '-') ++(*str); return scanUnsignedInteger(str); } };
調整數組順序使奇數位於偶數前面
// 題目 21:調整數組順序使奇數位於偶數前面 // 題目:輸入一個整數數組,實現一個函數來調整該數組中數字的順序,使得所有 // 奇數位於數組的前半部分,所有偶數位於數組的后半部分。 class class_21 { void ReorderOddEvent(int *pData,int length){ if(pData == nullptr || length == 0) { return; } int *pBegin = pData; int *pEnd = pData+length-1; while (pBegin<pEnd) { // 向后移動pBegin,直到它指向偶數 while (pBegin<pEnd && ((*pBegin)&0x1 )!=0) { pBegin++; } // 向前移動pEnd,直到它指向奇數 while(pBegin < pEnd && (*pEnd & 0x1) == 0) { pEnd --; } if(pBegin < pEnd) { int temp = *pBegin; *pBegin = *pEnd; *pEnd = temp; } } } };
鏈表中倒數第k個結點
/* 題目22: :鏈表中倒數第k個結點 // 題目:輸入一個鏈表,輸出該鏈表中倒數第k個結點。為了符合大多數人的習慣, // 本題從1開始計數,即鏈表的尾結點是倒數第1個結點。例如一個鏈表有6個結點, // 從頭結點開始它們的值依次是1、2、3、4、5、6。這個鏈表的倒數第3個結點是 // 值為4的結點。 */ class class_22{ ListNode* FindKthToTail(ListNode* pListHead, unsigned int k) { if(pListHead == nullptr || k == 0) return nullptr; ListNode *pAhead = pListHead; ListNode *pBehind = nullptr; for(unsigned int i = 0; i < k - 1; ++ i) { if(pAhead->m_pNext != nullptr) pAhead = pAhead->m_pNext; else { return nullptr; } } pBehind = pListHead; while(pAhead->m_pNext != nullptr) { pAhead = pAhead->m_pNext; pBehind = pBehind->m_pNext; } return pBehind; } };
一個鏈表中包含環,如何找出環的入口結點
/* 題目23 // 題目:一個鏈表中包含環,如何找出環的入口結點? 思路:快慢指針賽跑 先求出相遇點,然后根據數學公式 從相遇點出發和從head出發,相遇點就是 環入口點 */ class class_23 { ListNode *detectCycle2(ListNode *head){ ListNode *fast=head; ListNode * slow=head; ListNode *meet=NULL; while (fast) { slow=slow->m_pNext; fast=fast->m_pNext; if(!fast){ return NULL; } fast=fast->m_pNext; if(fast==slow){ meet=fast; break; } } if(meet==NULL){ return NULL; } while (head&&meet) { if(head==meet){ return head; } head=head->m_pNext; meet=meet->m_pNext; } return NULL; } };
反轉鏈表
/* 題目24 反轉鏈表 // 題目:定義一個函數,輸入一個鏈表的頭結點,反轉該鏈表並輸出反轉后鏈表的 // 頭結點。 */ class class_24 { //鏈表轉置 ListNode * reverseList2(ListNode * head) { ListNode *new_head=NULL;//指向新鏈表頭節點的指針 while(head){ ListNode *next= head->m_pNext;//備份head->next head->m_pNext=new_head;//更新hea睨zhid->next; new_head=head;//移動new_head; head=next;//遍歷鏈表 } return new_head;//返回新鏈表頭節點 } // 5-4-3-2-1 //遞歸方法 1->2->3->4->5 5->4->3->2->1 1->2->3 3->2->1 1-> 3->2->1 ListNode * reverseList34(ListNode *head){ //終止條件,到了最后一個節點 if(head==NULL||head->m_pNext==NULL){ return head; } ListNode * next = head->m_pNext; head->m_pNext=NULL;//這一步是為了第一個節點指向null ListNode * cur = reverseList34(next); //這是關鍵一步,head的next A,反轉之后,正好A的next就是head next->m_pNext=head; return cur; } };
合並兩個排序鏈表
/* 題目25: 合並兩個排序鏈表 */ class class_25 { struct ListNode { int val; ListNode *next; ListNode(int x) : val(x), next(NULL) {} }; ListNode *mergeTwoLists(ListNode *l1,ListNode *l2){ ListNode temp_head(0); ListNode *pre=&temp_head; while (l1&&l2) { if(l1->val < l2->val){ pre->next=l1; l1=l1->next; } else{ pre->next=l2; l2=l2->next; } pre=pre->next; } if(l1){ pre->next=l1; } if(l2){ pre->next=l2; } return temp_head.next; } };
輸入兩棵二叉樹A,B,判斷B是不是A的子結構(注意:是子結構,不是子樹)
/* 題目26 輸入兩棵二叉樹A,B,判斷B是不是A的子結構(注意:是子結構,不是子樹) */ class class_26 { struct TreeNode{ int value; TreeNode *left; TreeNode *right; }; bool tree1HasTree2(TreeNode *t1,TreeNode*t2){ if(t1==nullptr){ return false; } if(t2==nullptr){ return true; } if(t1->value!=t2->value){ return false; } return tree1HasTree2(t1->left, t2->left) && tree1HasTree2(t1->right, t2->right); } bool HasSubTree(TreeNode *t1,TreeNode*t2){ bool result=false; if(t1 && t2){ result = tree1HasTree2(t1, t2); if(!result){ result=HasSubTree(t1->left, t2); } if(!result){ result = HasSubTree(t1->right, t2); } } return result; } };
操作給定的二叉樹,將其變換為源二叉樹的鏡像
/* 題目26 操作給定的二叉樹,將其變換為源二叉樹的鏡像。 */ class class_26_2 { struct TreeNode{ int value; TreeNode *left; TreeNode *right; }; void getMirroSubTree(TreeNode * left,TreeNode *right){ } void getMirroTree(TreeNode * root){ if(root==nullptr)return; if(!root->left && !root->right){ return; } TreeNode *left = root->left; TreeNode * right = root->right; TreeNode *temp= left; left = right; right=temp; if(root->left){ getMirroTree(root->left); } if(root->right){ getMirroTree(root->right); } } };
判斷二叉樹是不是對稱二叉樹
/* 題目28 判斷二叉樹是不是對稱二叉樹 請實現一個函數,用來判斷一顆二叉樹是不是對稱的。注意,如果一個二叉樹同此二叉樹的鏡像是同樣的,定義其為對稱的。 */ class class_28 { struct TreeNode{ int value; TreeNode *left; TreeNode *right; }; bool isSymmetrical(TreeNode* pRoot) { return isSymmetrical(pRoot, pRoot); } bool isSymmetrical(TreeNode* pRoot1, TreeNode* pRoot2) { if(pRoot1 == nullptr && pRoot2 == nullptr) return true; if(pRoot1 == nullptr || pRoot2 == nullptr) return false; if(pRoot1->value != pRoot2->value) return false; return isSymmetrical(pRoot1->left, pRoot2->right) && isSymmetrical(pRoot1->right, pRoot2->left); } };
順時針打印矩陣
分析:
簡單看看螺旋矩陣,其規律就是一圈一圈的往里繞,因此我們可以想象有一條貪吃蛇,它很聽話,如果要出格子或者碰到格子里有數的時候就向右轉一下,然后在它路過的格子里順序填充上數字就好
棧的壓入、彈出序列
/* 第31題 棧的壓入、彈出序列 輸入兩個整數序列,第一個序列表示棧的壓入順序,請判斷第二個序列是否可能為該棧的彈出順序。假設壓入棧的所有數字均不相等。例如序列1,2,3,4,5是某棧的壓入順序,序列4,5,3,2,1是該壓棧序列對應的一個彈出序列,但4,3,5,1,2就不可能是該壓棧序列的彈出序列。(注意:這兩個序列的長度是相等的) */ class class_31{ bool checkIsValied(queue<int>&order){ std::stack<int>S;//模擬戰 int n=(int)order.size();//n為序列長度,將1-n按順序入站 for (int i=1; i<=n; i++) { S.push(i); //將i入站 while (!S.empty()&&S.top()==order.front()) { S.pop(); order.pop();//只要S不空且隊列頭部與戰頂相同,即彈出元素 } } if(S.empty()!=false){//如果最終棧不空,則說明序列不合法 return false; } return true; } };
從上往下打印二叉樹
/* 第32題 從上往下打印二叉樹 從上往下打印出二叉樹的每個節點,同層節點從左至右打印。 換行打印 */ class class_32{ struct TreeNode{ int value; TreeNode *left; TreeNode *right; }; //按層打印 void Print(TreeNode* root){ queue<TreeNode*> qu; int level=0; int toBeBehind=1; qu.push(root); while (qu.empty()==false) { TreeNode * node = qu.front(); qu.pop(); printf("%d",root->value); if (node->left) { qu.push(node->left); level++; } if(node->right){ qu.push(node->right); level++; } toBeBehind--; if(toBeBehind==0){ toBeBehind = level; level=0; printf("\n"); } } }} ;
Z打印二叉樹
//Z打印二叉樹 class class_32_2 { struct TreeNode{ int value; TreeNode *left; TreeNode *right; }; void PrintZ(TreeNode* root){ if(root==nullptr) return; vector<vector<TreeNode*>>array; stack<TreeNode*>s1; stack<TreeNode*>s2; int layer=0; s1.push(root); while (s1.empty()==false || s2.empty()==false) { vector<TreeNode*> vec; while (s1.empty()==false && layer%2==0) { TreeNode *node=s1.top(); s1.pop(); vec.push_back(node); if(node->right){ s2.push(node->right); } if(node->left){ s2.push(node->left); } } while (s2.empty()==false && layer%2==1) { TreeNode *node=s2.top(); s2.pop(); vec.push_back(node); if(node->left){ s2.push(node->left); } if(node->left){ s2.push(node->right); } } if(vec.size()){ array.push_back(vec); } layer++; } for (int i=0; i<array.size(); i++) { vector<TreeNode*>vec = array[i]; for (int j=0; j<vec.size(); j++) { printf("%d ",vec[j]->value); } printf("\n"); } } };
二叉樹搜索的后序遍歷序列
/* 第33題 二叉樹搜索的后序遍歷序列 輸入一個整數數組,判斷該數組是不是某二叉搜索樹的后序遍歷的結果。 如果是則輸出Yes,否則輸出No。假設輸入的數組的任意兩個數字都互不相同。 如 {5,7,6,9,11,10,8} 返回true,{7,4,6,5} 返回false */ class class_33{ bool verifySqueenceOfBST(vector<int>vec){ if(vec.size()==0){ return false; } int i=0; int begin=0; int end = (int)vec.size()-1; int root = vec[end]; ////左子樹的節點的值都比根節點小 for(i=begin;i<end;i++){ if(vec[i]>root){ ////如果大於root節點,直接跳出循環,判斷該樹是不是只有右子樹 break; } } // i 為右子樹的第一個節點 ,j為右子樹的最后一個節點 for (int j=i; j<end; j++) { if(vec[j]<root){ return false; } } //到此 左右子樹暫時符合條件,下面分別檢測左子樹 和右子樹 bool left = true; vector<int>leftVec; for(int j=0;j<i;j++){ leftVec.push_back(vec[j]); } if(leftVec.size()>0){//有左子樹 left= verifySqueenceOfBST(leftVec); } bool right = true; vector<int>rightVec; for (int j=i; j<vec.size()-1; j++) { rightVec.push_back(vec[j]); } if(rightVec.size()>0){ right= verifySqueenceOfBST(rightVec); } return left && right; } };
二叉樹中和為某一值的路徑
/* 第34題 二叉樹中和為某一值的路徑 輸入一顆二叉樹的跟節點和一個整數,打印出二叉樹中結點值的和為輸入整數的所有路徑。路徑定義為從樹的根結點開始往下一直到葉結點所經過的結點形成一條路徑 */ class class_34{ public: struct TreeNode{ int val; TreeNode *left; TreeNode *right; }; vector<vector<int>>pathSum(TreeNode *root,int sum){ vector<vector<int>>result; vector<int>path; int path_value=0;//過程中累加的和 preorder(root, path_value, sum, path, result); return result; } private: void preorder(TreeNode *node,int&path_value,int sum,vector<int>&path, vector<vector<int>>&result){ if(node==NULL) return; path_value+=node->val; path.push_back(node->val); if(node->left==NULL&&node->right==NULL){ if(path_value==sum){ result.push_back(path); } } preorder(node->left, path_value, sum, path, result); preorder(node->right, path_value, sum, path, result); path_value-=node->val; path.pop_back();//遍歷完畢,將節點從棧中取出 } };
復雜鏈表的復制
/* 第35題 復雜鏈表的復制 輸入一個復雜鏈表(每個節點中有節點值,以及兩個指針,一個指向下一個節點,另一個特殊指針指向任意一個節點),返回結果為復制后復雜鏈表的head。 */ class class_35{ class Node { public: int val; Node* next; Node* random; Node() {} Node(int _val, Node* _next, Node* _random) { val = _val; next = _next; random = _random; } }; Node* copyRandomList(Node* head) { if(head==nullptr)return nullptr; Node * p = head; //把原來的鏈表和新的鏈表 連接到一起 // 1->1'->2->2'->null while (p) { int value = p->val; Node * cpNode = new Node(); cpNode->val=value; cpNode->random=nullptr; cpNode->next = p->next; p->next=cpNode; p=cpNode->next; } //賦值 隨機鏈表 p=head; while (p) { Node * random = p->random; Node *cp = p->next; cp->random =random?random->next:nullptr; p=cp->next; } //拆分出 新的鏈表 p=head; Node * newHead= head->next; while (p) { Node * cp=p->next; //記錄下一個節點 Node *oldNext=cp->next; Node * cpNext= oldNext? oldNext->next:nullptr; p->next=oldNext; cp->next=cpNext; p=oldNext; cp=cpNext; } return newHead; } };
二叉搜索樹與雙向鏈表
/* 第36題 二叉搜索樹與雙向鏈表 輸入一棵二叉搜索樹,將該二叉搜索樹轉換成一個排序的雙向鏈表。要求不能創建任何新的結點,只能調整樹中結點指針的指向。 */ class class_36{ public: struct TreeNode{ int val; TreeNode *left; TreeNode *right; }; void ConvertNode(TreeNode *node,TreeNode *& last){ if(node==nullptr){ return; } //中序遍歷 TreeNode *current=node;//保存 ///左子樹 if(current->left){ ConvertNode(current->left,last); } current->left =last; //當前的左指針指向last if(last){ last->right=current; } //最后一個節點 更新為 current last=current; //右子樹 if(current->right){ ConvertNode(current->right, last); } } TreeNode * Convert(TreeNode * root){ TreeNode * last = nullptr; ConvertNode(root, last); //返回頭節點 TreeNode * p = last; while (p&&p->left) { p = p->left; } return p; } };
序列化二叉樹
/* 第36_2題 序列化二叉樹 請實現兩個函數,分別用來序列化和反序列化二叉樹 */ class class36_2 { public: struct TreeNode { int val; TreeNode *left; TreeNode *right; TreeNode(int x) : val(x), left(NULL), right(NULL) {} }; string intToString(int num){ string result =""; if(num==0){ result.push_back('0'); } bool isfu=false; if(num<0){ num=-num; isfu=true; } while (num) { char ch= num%10 +'0'; result.push_back(ch); num=num/10; } string newresult; if(isfu){ newresult.push_back('-'); } for(int i=(int)result.length()-1;i>=0;i--){ newresult.push_back(result[i]); } newresult+="_"; return newresult; } // 123 int stringToint(string s){ if(s.length()==0){ return 0; } char ch = s[0]; int flag=0; if(ch=='-'){//負數 flag=1; } int num=0; for (int i = flag?1:0; i<s.size(); i++) { int t = s[i]-'0'; num = num*10+t; } return flag? -num:num; } void BianMa(TreeNode *root,string &s){ if(root==nullptr){ s+="#_"; return; } s+=intToString(root->val); BianMa(root->left, s); BianMa(root->right, s); } void jieMa(vector<string>&vec,TreeNode *&root,int &i){ if(i==vec.size()){ return; } if(vec[i]=="#"){ root=nullptr; return; } string s = vec[i]; int num= stringToint(s); if(root==nullptr){ root = new TreeNode(num); } else { root->val=num; } i+=1; jieMa(vec, root->left, i); i+=1; jieMa(vec, root->right, i); } // Encodes a tree to a single string. string serialize(TreeNode* root) { string s=""; BianMa(root, s); return s; } //1_2_3_#_4_ // Decodes your encoded data to tree. TreeNode* deserialize(string data) { string item=""; vector<string> vec; for (int i=0; i<data.size(); i++) { char ch =data[i]; if(ch=='_'){ vec.push_back(item); item=""; } else { item+=ch; } } TreeNode *root=nullptr; int i=0; jieMa(vec, root, i); return root; } };
字符串的排列
/* 37 題 字符串的排列 輸入一個字符串,按字典序打印出該字符串中字符的所有排列。例如輸入字符串abc,則打印出由字符a,b,c所能排列出來的所有字符串abc,acb,bac,bca,cab和cba。 思路: 回溯法 */ class class_37{ void preCHangeSUb(char *p,char *begin){ if(*begin=='\0'){ printf("%s \n",p); } for (char *ch=begin; *ch !='\0'; ch++) { char temp = *begin; *begin=*ch; *ch=temp; preCHangeSUb(p, begin+1); temp = *begin; *begin=*ch; *ch=temp; } } void preChange(char * p){ if(p==nullptr){ return; } preCHangeSUb(p, p); } };
數字出現的次數超過數組長度的一半
/* 39 題 數組中有一個數字出現的次數超過數組長度的一半,請找出這個數字。 例如輸入一個長度為9的數組{1,2,3,2,2,2,5,4,2}。 由於數字2在數組中出現了5次,超過數組長度的一半,因此輸出2。如果不存在則輸出0。 */ class class_39{ int MoreThanHalfNum(int * numbers,int length){ int result=numbers[0]; int times=1; for (int i=1; i<length; i++) { if(times==0){ result = numbers[i]; times=1; } else if(numbers[i]==result){ times++; } else { times--; } } return result; } };
最小的k個數
/* 40題: 最小的k個數 輸入n個整數,找出其中最小的K個數。例如輸入4,5,1,6,2,7,3,8這8個數字,則最小的4個數字是1,2,3,4,。 */ /* 方法:最大堆 */ class class_40{ void findKthLargest(std::vector<int>&nums,int k) { //構建最大堆 std::priority_queue<int, std::vector<int> > Q; for (int i=0; i<nums.size(); i++) { if(Q.size()<k){ Q.push(nums[i]); } else if(nums[i]<Q.top()){ Q.pop(); Q.push(nums[i]); } } //遍歷輸出即可 vector<int>vec; while (Q.empty()==false) { vec.push_back(Q.top()); Q.pop(); } auto it = vec.begin(); for (; it!=vec.end(); it++) { printf("%d",*it); } } };
數據流中的中位數
/* 41題 數據流中的中位數 如何得到一個數據流中的中位數?如果從數據流中讀出奇數個數值,那么中位數就是所有數值排序之后位於中間的數值。如果從數據流中讀出偶數個數值,那么中位數就是所有數值排序之后中間兩個數的平均值。我們使用Insert()方法讀取數據流,使用GetMedian()方法獲取當前讀取數據的中位數 */ class class_41 { public: /** initialize your data structure here. */ class_41() { } void addNum(int num) { //bigqueue中保存最小的集合,small_queue保存最大的集合 if(big_queue.size()==0){ big_queue.push(num); } else { if(big_queue.size()==small_queue.size()){ if(num>big_queue.top()){ small_queue.push(num); } else { big_queue.push(num); } } else if(big_queue.size()>small_queue.size()){ if(big_queue.top()<num){ small_queue.push(num); } else { int top = big_queue.top(); big_queue.pop(); big_queue.push(num); small_queue.push(top); } } else if(big_queue.size()<small_queue.size()){ if(small_queue.top()>num){ big_queue.push(num); } else { int top = small_queue.top(); small_queue.pop(); small_queue.push(num); big_queue.push(top); } } } } double findMedian() { if(big_queue.size()==small_queue.size()){ return (big_queue.top()+small_queue.top())*0.5; } else if(big_queue.size()>small_queue.size()){ return big_queue.top(); } else { return small_queue.top(); } } private: std::priority_queue<double> big_queue; std::priority_queue<double, std::vector<double>, std::greater<double> > small_queue; };
連續子數組的最大和
/* 42題 連續子數組的最大和 思路:動態規划 */ class class_42 { public: int maxSubArray(vector<int>& nums) { // i的狀態為 以第i個數為結尾的最大和 vector<int>dp(nums.size()); dp[0]=nums[0]; int maxNum=dp[0]; for (int i=1; i<nums.size(); i++) { dp[i]= max(dp[i-1]+nums[i],nums[i]); if(maxNum<dp[i]){ maxNum=dp[i]; } } return maxNum; } };
整數中1出現的次數
/* 43 題 整數中1出現的次數 與數學關系比較大,需要找數學規律 */ //思路: /* 在分析之前,首先需要知道一個規律: 從 1 至 10,在它們的個位數中,數字1出現了 1 次。 從 1 至 100,在它們的十位數中,數字1出現了 10 次。 從 1 至 1000,在它們的百位數中,數字1出現了 100 次。 依此類推,從 1 至 10i,在它們右數第二位中,數字1出現了10 ^ (i - 1)次。 對於 n = 2134,要找到從1 ~ 2134這2134個數字中所有1的個數。我們可以對2134進行逐位分析: (1)在個位上,從1~2130,包含213個10,因此數字1出現了213次,剩下的數字2131、2132、2133、2134中個位數上只有2131包含樹脂字1,剩下的都不包含。所以個位數上的數字1的總數為213 + 1 = 214。 (2)在十位上,從1 ~ 2100,包含了21個100,因此數字1出現了21 * 10 = 210次,剩下的數字從2101 ~ 2134,只有2110 ~ 2119這10個數字中十位的數字為1,所以十位上的數字1的總數為210 + 10 = 220。 (3)在百位上,從1 ~ 2000,包含了2個1000,因此數字1出現了2 * 100 = 200次,剩下的數字從2001 ~ 2134,只有2100 ~ 2134這35個數字中的百位的數字為1,所以百位數上數字1的總數為200 + 35= 235。 (4)在千位上,包含了0個10000,因此數字1出現了0 * 1000 = 0次,剩下的數字中只有1000 ~ 1999這1000個數字中的千位的數字為1,所以千位上的數字1的總數為1000。 因此從1 ~ 2134這n個數字中,數字出現的總的次數為 214 + 220 + 235 +1000 = 1669。 總結一下以上的步驟,可以得到這么一個規律: 對於數字n,計算它的第i(i從1開始,從右邊開始計數)位數上包含的數字1的個數: 假設第i位上的數字為x的話,則 1.如果x > 1的話,則第i位數上包含的1的數目為:(高位數字 + 1)* 10 ^ (i-1) (其中高位數字是從i+1位一直到最高位數構成的數字) 2.如果x < 1的話,則第i位數上包含的1的數目為:(高位數字 )* 10 ^ (i-1) 3.如果x == 1的話,則第i位數上包含1的數目為:(高位數字) * 10 ^ (i-1) +(低位數字+1) (其中低位數字時從第i - 1位數一直到第1位數構成的數字) */ // class class_43 { int NumberOfDigitOne(int n) { if( n < 0) return 0; int i = 1; int high = n; int cnt = 0; while(high != 0) { high = n / pow(10 ,i);//high表示當前位的高位 int temp = n / pow(10, i - 1); int cur = temp % 10;//cur表示第i位上的值,從1開始計算 int low = n - temp * pow(10, i - 1);//low表示當前位的低位 if(cur < 1) { cnt += high * pow(10, i - 1); } else if(cur > 1) { cnt += (high + 1) * pow(10 ,i - 1); } else { cnt += high * pow(10, i - 1); cnt += (low + 1); } i++; } return cnt; } };
數字序列中某一位的數字
/* 44題 數字序列中某一位的數字 題目要求: 數字以01234567891011121314...的格式排列。在這個序列中,第5位(從0開始計)是5,第13位是1,第19位是4。求任意第n為對應的數字。 // 解題思路: 與43題類似,都是數學規律題。如果用遍歷的方式,思路代碼都很簡單,但速度較慢。更好的方式是借助於數字序列的規律,感覺更像是數學題。步驟大致可以分為如下三部分: 以第15位數字2為例(2隸屬與12,兩位數,位於12從左側以0號開始下標為1的位置) 步驟1:首先確定該數字是屬於幾位數的; 如果是一位數,n<9;如果是兩位數,n<9+90*2=189; 說明是兩位數。 步驟2:確定該數字屬於哪個數。10+(15-10)/2= 12。 步驟3:確定是該數中哪一位。15-10-(12-10)*2 = 1, 所以位於“12”的下標為1的位置,即數字2。 以第1001位數字7為例 步驟1:首先確定該數字是屬於幾位數的; 如果是一位數,n<9;如果是兩位數,n<9+90*2=189;如果是三位數,n<189+900*3=2889; 說明是三位數。 步驟2:確定該數字屬於哪個數。100+(1001-190)/3= 370。 步驟3:確定是該數中哪一位。1001-190-(370-100)*3 = 1,所以位於“370”的下標為1的位置,即數字1。 */ class class_44{ public: //求的數字長度的所有數的總數 int lengthSum(int length){ int count = 9; for (int i=1; i<length; i++) { count *= 10; } return count * length; } int digitAtIndex(int index){ if(index<0) return -1; if(index<10) return index; int curIndex=10;//當前多少個數 int length=2; int boundNum = 10; //看看index在幾位數中間 while (curIndex+lengthSum(length)<index) { curIndex+=lengthSum(length); boundNum *= 10; length++; } //判斷index在n位數中屬於哪個數 int addNum = (index-curIndex)/length; int curNum = boundNum + addNum; //判斷index在 這個curNum中的第幾位 int finalIndex= index-curIndex-addNum*length; string s = to_string(curNum).substr(finalIndex,1); return s[0]-'0'; } };
把數組排成最小的數
/* 45題 把數組排成最小的數 輸入一個正整數數組,把數組里所有數字拼接起來排成一個數,打印能拼接出的所有數字中最小的一個。 例如輸入數組{3,32,321},則打印出這三個數字能排成的最小數字為321323 思路:定義一個字符串比較規則,挺巧妙 */ class class_45 { public: string PrintMinNumber(vector<int> numbers) { string res=""; int length = (int)numbers.size(); if(length==0) return ""; sort(numbers.begin(),numbers.end(),cmp); for(int i=0;i<length;i++) res += to_string(numbers[i]); return res; } static bool cmp(int a,int b){ string A = to_string(a)+to_string(b); string B = to_string(b)+to_string(a); return A<B; } };
把數字翻譯成字符串
/* 46題: 把數字翻譯成字符串 給定一個數字,按照如下規則翻譯成字符串: 0->a 1->b ... 25->z 因此一個數字可能有多種翻譯。例如,12258有5種不同的翻譯,bccfi,bwfi,bczi,mcfi,mzi。 請實現一個函數,計算數字有多少種翻譯方法。 思路: 動態規划 */ class class_46 { int GetTranslationCount22(string s){ /* 動態規划四個條件 1 子問題:求1位數的表達,2位數 2 邊界條件: 最后一位值為1 3 狀態: 第i位有幾種表示 4公式: f(i)=f(i+1)+(0,1)*f(i+2) */ vector<int> vec(0); for (int i= (int)s.length(); i>0; i--) { vec.push_back(0); } vec[s.length()]=0; vec[s.length()-1]=1; for (int i= (int)s.length()-2; i>=0; i--) { int temp=(s[i]-'0')*10 + (s[i+1]-'0'); int value=0; if(temp>=10&&temp<=25) { value=1; } vec[i] = vec[i+1] + value * vec[i+2]; } return vec[0]; } };
最大價值的禮物
/* 47題 在一個 m*n 的棋盤中的每一個格都放一個禮物,每個禮物都有一定的價值(價值大於0).你可以從棋盤的左上角開始拿各種里的禮物,並每次向左或者向下移動一格,直到到達棋盤的右下角。給定一個棋盤及上面個的禮物,請計算你最多能拿走多少價值的禮物? 思路: 很簡單的動態規划 */ class class_47 { int getMaxValue_solution(vector<vector<int>> &nums){ vector<vector<int>>vec; for (int i=0; i<nums.size(); i++) { vec.push_back(vector<int>()); for (int j=0; j<nums[0].size(); j++) { vec[i].push_back(0); } } int rows = (int)nums.size(); int columns= (int)nums[0].size(); for (int i=0; i<rows; i++) { for(int j=0;j<columns;j++){ int left=0; int up=0; if(i>0){ up = vec[i-1][j]; } if(j>0){ left = vec[i][j-1]; } vec[i][j]= max(up,left)+nums[i][j]; } } return vec[rows-1][columns-1]; } };
最長不含重復字符的子字符串
/* 題目48、 最長不含重復字符的子字符串 */ // abcdfsdfefg class class_48{ //方法一:哈希表 int lengthOfLongestSubstring(string s){ string word=""; int begin=0; char temp [128]={0}; int result=0; for (int i=0; i<s.length(); i++) { temp[s[i]]++; if(temp[s[i]]==1){ word+=s[i]; if(word.length()>result){ result = (int)word.length(); } } else { while (begin<i && temp[s[i]]>1) { temp[s[begin]]--; begin++; } word=""; for (int j= begin; j<=i; j++) { word+=s[j]; } } } return result; } //方法二: 動態規划 // abdacadfsdfefg int lengthOfLongestSubstring2(string s){ /* 動態規划四個條件: 子問題: 求一個字符的字符串,求二個字符 邊界值: 一個字符的個數是1 狀態:i的狀態為 以第i個字符為結尾的最長子串 1 如果當前字符沒有在之前掃描過的字符串中出現過,當前的就為 上次的f(i-1)+1 2 如果當前的字符 在上一次出現過 2.1 2個相同字符的距離超過 上一個狀態的時候,f(i)=f(i-1)+1 2.2 2個相同字符的距離 沒有超過上一個狀態的時候,f(i)=2者的距離 */ if(s.length()==0||s.length()==1){ return (int)s.length(); } vector<int>vec; //記錄最后一次某個字符出現的位置 map<char,int> charPosition; charPosition[s[0]]=0; vec.push_back(1); int maxLength0=0; for (int i=1; i<s.length(); i++) { char ch = s[i]; if( charPosition.find(ch)==charPosition.end()){//沒出現過 vec.push_back(vec[i-1]+1); } else {//出現過 //2個相同字符的距離 沒有超過上一個狀態的時候 if(i - charPosition[ch] <=vec[i-1]){ vec.push_back(i - charPosition[ch]); } else { //2個相同字符的距離超過 上一個狀態的時候 vec.push_back(vec[i-1]+1); } } if(vec[i]>maxLength0){ maxLength0 = vec[i]; } charPosition[ch]=i; } return maxLength0; } };
丑數
/* 49題 丑數 把只包含質因子2、3和5的數稱作丑數(Ugly Number)。例如6、8都是丑數,但14不是, 因為它包含質因子7。 習慣上我們把1當做是第一個丑數。求按從小到大的順序的第N個丑數 */ class class_49{ int Min2(int num1, int num2, int num3) { int min = num1 < num2 ? num1 : num2; min = min < num3 ? min : num3; return min; } int nthUglyNumber2(int index) { if (index <= 0) { return 0; } vector<int> uglyNumbers(index); uglyNumbers[0] = 1; int nextUglyIndex = 1; int multiply2 = 0;//記錄 乘以2第一個大於 目前最大丑數的 位置 int multiply3 = 0; int multiply5 = 0; int min = 0; while (nextUglyIndex < index) //1 2 3 { min = Min2(uglyNumbers[multiply2] * 2, uglyNumbers[multiply3] * 3, uglyNumbers[multiply5] * 5); uglyNumbers[nextUglyIndex] = min; while (uglyNumbers[multiply2] * 2 <= uglyNumbers[nextUglyIndex]) { multiply2++; } while (uglyNumbers[multiply3] * 3 <= uglyNumbers[nextUglyIndex]) { multiply3++; } while (uglyNumbers[multiply5] * 5 <= uglyNumbers[nextUglyIndex]) { multiply5++; } nextUglyIndex++; } int result = uglyNumbers[index - 1]; return result; } };
第一次只出現一次的字符的索引
/* 第50題: 第一次只出現一次的字符的索引 */ //bcdefab class class_50{ /* 第一次只出現一次的字符的索引 在一個字符串(0<=字符串長度<=10000,全部由字母組成)中找到第一個只出現一次的字符,並返回它的位置, 如果沒有則返回 -1(需要區分大小寫) */ int firstUniqChar(string s) { int temp[256]={0}; map<char, int> _map; for (int i=0; i<s.length(); i++) { temp[s[i]]++; _map[s[i]]=i; } for (int i=0; i<s.length(); i++) { if(temp[s[i]]==1){ return _map[s[i]]; } } return -1; } };
字符流中第一個不重復的字符【不是求索引】
/* 50題-2 字符流中第一個不重復的字符【不是求索引】 請實現一個函數用來找出字符流中第一個只出現一次的字符。 例如,當從字符流中只讀出前兩個字符"go"時,第一個只出現一次的字符是"g"。 當從該字符流中讀出前六個字符“google"時,第一個只出現一次的字符是"l"。 */ class class_50_2{ class CharStatistics { public: CharStatistics() : index(0) { for(int i = 0; i < 256; ++i) occurrence[i] = -1; } void Insert(char ch) { if(occurrence[ch] == -1) occurrence[ch] = index; else if(occurrence[ch] >= 0) occurrence[ch] = -2; index++; } char FirstAppearingOnce() { char ch = '\0'; int minIndex = numeric_limits<int>::max(); for(int i = 0; i < 256; ++i) { if(occurrence[i] >= 0 && occurrence[i] < minIndex) { ch = (char) i; minIndex = occurrence[i]; } } return ch; } private: // occurrence[i] = -1: 字符沒有找到; // occurrence[i] = -2: 字符找到許多次 // occurrence[i] >= 0: 字符只被找到一次 int occurrence[256]; int index; }; };
數組中的逆序對
/* 51題 數組中的逆序對 在數組中的兩個數字,如果前面一個數字大於后面的數字,則這兩個數字組成一個逆序對。輸入一個數組,求出這個數組中的逆序對的總數P。並將P對1000000007取模的結果輸出。 即輸出P%1000000007 思路:二叉排序樹 */ class class_51 { public: struct BSTNode { int val; int count;//左子樹的數目 BSTNode *left; BSTNode *right; BSTNode(int x) : val(x), left(NULL), right(NULL), count(0) {} }; std::vector<int> countSmaller(std::vector<int>& nums) { std::vector<int> result; std::vector<BSTNode *> node_vec; std::vector<int> count; for (int i = (int)nums.size() - 1; i >= 0; i--){ node_vec.push_back(new BSTNode(nums[i])); } count.push_back(0); for (int i = 1; i < node_vec.size(); i++){ int count_small = 0; BST_insert(node_vec[0], node_vec[i], count_small); count.push_back(count_small); } for (int i = (int)node_vec.size() - 1; i >= 0; i--){ delete node_vec[i]; result.push_back(count[i]); } return result; } private: void BST_insert(BSTNode *node, BSTNode *insert_node, int &count_small){ if (insert_node->val <= node->val){ node->count++; if (node->left){ BST_insert(node->left, insert_node, count_small); } else{ node->left = insert_node; } } else{ count_small += node->count + 1; if (node->right){ BST_insert(node->right, insert_node, count_small); } else{ node->right = insert_node; } } } };
兩個鏈表的第一個公共結點
/* 52題 兩個鏈表的第一個公共結點 輸入兩個鏈表,找出它們的第一個公共結點。 1-2-3-4-5 2-4-5-6-7 */ class class_52 { ListNode* FindFirstCommonNode(ListNode *pHead1, ListNode *pHead2) { unsigned int nLength1 = GetListLength(pHead1); unsigned int nLength2 = GetListLength(pHead2); int nLengthDif = nLength1 - nLength2; ListNode* pListHeadLong = pHead1; ListNode* pListHeadShort = pHead2; if(nLength2 > nLength1) { pListHeadLong = pHead2; pListHeadShort = pHead1; nLengthDif = nLength2 - nLength1; } for(int i = 0; i < nLengthDif; ++i) pListHeadLong = pListHeadLong->m_pNext; while((pListHeadLong != nullptr) && (pListHeadShort != nullptr) && (pListHeadLong != pListHeadShort)) { pListHeadLong = pListHeadLong->m_pNext; pListHeadShort = pListHeadShort->m_pNext; } ListNode* pFisrtCommonNode = pListHeadLong; return pFisrtCommonNode; } unsigned int GetListLength(ListNode* pHead) { unsigned int nLength = 0; ListNode* pNode = pHead; while(pNode != nullptr) { ++nLength; pNode = pNode->m_pNext; } return nLength; } };
數字在排序數組中出現的次數
/* 53題 數字在排序數組中出現的次數 題目:統計一個數字在排序數組中出現的次數。例如輸入排序數組{1,2,3,3,3,3,4,5}和數字3,由於3在這個數組中出現了4次,因此輸出4。 */ class class_53 { public: int leftSearch(vector<int>&nums,int target){ int begin=0; int end=(int)nums.size()-1; int index=-1; while (begin<=end) { int mid = (begin+end)/2; if(target==nums[mid]){ if(mid==0||target>nums[mid-1]){ index=mid; break; } else { end = mid-1; } } else if(target<nums[mid]){ end=mid-1; } else if(target>nums[mid]){ begin=mid+1; } } return index; } int rightSearch(vector<int>&nums,int target){ int begin=0; int end=(int)nums.size()-1; int index=-1; while (begin<=end) { int mid = (begin+end)/2; if(target==nums[mid]){ if(mid==nums.size()-1||target<nums[mid+1]){ index=mid; break; } else { begin = mid+1; } } else if(target<nums[mid]){ end=mid-1; } else if(target>nums[mid]){ begin=mid+1; } } return index; } int searchRange(vector<int>& nums, int target) { int a = leftSearch(nums, target); int b = rightSearch(nums, target); if(a!=-1 && b!=-1){ return b-a+1; } return -1; } };
0-n-1中缺失的數字
/* 53題目-2:0-n-1中缺失的數字 長度為n-1的遞增排序數組中的所有數字都是唯一的,並且每個數字都在范圍0到n-1之內。在范圍0到n-1的n個數字中有且只有一個數字不在該數組中,請找出這個數字。 思路:二分查找 */ class class53_2{ int GetMissingNumber(const int* numbers, int length) { if(numbers == nullptr || length <= 0) return -1; int left = 0; int right = length - 1; while(left <= right) { int middle = (right + left) >> 1; if(numbers[middle] != middle) { if(middle == 0 || numbers[middle - 1] == middle - 1) return middle; right = middle - 1; } else{ left = middle + 1; } } if(left == length) return length; // 無效的輸入,比如數組不是按要求排序的, // 或者有數字不在0到n-1范圍之內 return -1; } };
二叉搜索樹中第K小的元素
/* 54題目: 二叉搜索樹中第K小的元素 思路:中序遍歷 */ class class_54 { public: struct TreeNode { int val; TreeNode *left; TreeNode *right; TreeNode(int x) : val(x), left(NULL), right(NULL) {} }; void ksmallSub(TreeNode * root,int &i,int k,int &value,bool &find){ if(root==nullptr || find){ return; } if(root->left){ ksmallSub(root->left, i, k,value,find); } if(i==k-1&&find==false){ value = root->val; find = true; return ; } i=i+1; if(root->right){ ksmallSub(root->right, i, k,value,find); } } int kthSmallest(TreeNode* root, int k) { int i=0; int value = 0; bool find = false; ksmallSub(root,i,k,value,find); return value; } };
二叉樹的深度
/* 55題 二叉樹的深度 */ class class_55 { public: struct TreeNode { int val; TreeNode *left; TreeNode *right; TreeNode(int x) : val(x), left(NULL), right(NULL) {} }; int TreeDepth(TreeNode *root){ if(root==nullptr){ return 0; } int left = TreeDepth(root->left); int right = TreeDepth(root->right); return (left>right)?(left+1):(right+1); } };
判斷是否為平衡二叉樹
/* 55-2題 判斷是否為平衡二叉樹 //思路: 后序遍歷 */ class class_55_2{ struct TreeNode { int val; TreeNode *left; TreeNode *right; TreeNode(int x) : val(x), left(NULL), right(NULL) {} }; /* 方法一:根據求深度來遍歷(會重復計算很多次,效率不高) */ int TreeDepth(TreeNode *root){ if(root==nullptr){ return 0; } int left = TreeDepth(root->left); int right = TreeDepth(root->right); return (left>right)?(left+1):(right+1); } bool isBalanced2(TreeNode* root) { if(root==nullptr){ return true; } int left = TreeDepth(root->left); int right = TreeDepth(root->right); int diff = left - right; if(diff>1 || diff<-1){ return false; } return isBalanced(root->left) && isBalanced(root->right); } /* 方法二:后序遍歷 */ bool isBlancedSub(TreeNode * root,int &depth){ if(root==nullptr) { depth=0; return true; } if(root->left==nullptr&&root->right==nullptr){ depth = 1; return true; } int depth1=0; int depth2=0; bool result = isBlancedSub(root->left, depth1); bool result2 = isBlancedSub(root->right, depth2); if(result&&result2){ int diff = depth1-depth2; if(abs(diff)<=1){ depth = max(depth1, depth2)+1; return true; } } return false; } bool isBalanced(TreeNode* root) { int depth=0; return isBlancedSub(root, depth); } };
數組中只出現一次的兩個數字
/* 56 題 面試題56:數組中只出現一次的兩個數字 題目要求: 一個整數數組里除了兩個數字出現一次,其他數字都出現兩次。請找出這兩個數字。要求時間復雜度為o(n),空間復雜度為o(1)。 解題思路: 這道題可以看成“數組中只出現一次的一個數字”的延伸。如果所有數字都出現兩次,只有一個數字是出現1次,那么可以通過把所有所有進行異或運算解決。因為x^x = 0。 但如果有兩個數字出現一次,能否轉化成上述問題?依舊把所有數字異或,最終的結果就是那兩個出現一次的數字a,b異或的結果。因為a,b不想等,因此結果肯定不為0,那么結果的二進制表示至少有一位為1,找到那個1的位置p,然后我們就可以根據第p位是否為1將所有的數字分成兩堆,這樣我們就把所有數字分成兩部分,且每部分都是只包含一個只出現一次的數字、其他數字出現兩次,從而將問題轉化為最開始我們討論的“數組中只出現一次的一個數字”問題。 實例分析(以2,4,3,6,3,2,5,5為例): 相關數字的二進制表示為: 2 = 0010 3 = 0011 4 = 0100 5 = 0101 6 = 0110 步驟1 全體異或:2^4^3^6^3^2^5^5 = 4^6 = 0010 步驟2 確定位置:對於0010,從右數的第二位為1,因此可以根據倒數第2位是否為1進行分組 步驟3 進行分組:分成[2,3,6,3,2]和[4,5,5]兩組 步驟4 分組異或:2^3^6^3^2 = 6,4^5^5 = 4,因此結果為4,6。 */ class class56 { public: int findIndexOne(int p){ int index=0; while ((p&1)==0) { p=p>>1; index++; } return index; } int isBitOne(int num,int index){ return (num>>index)&1; } vector<int> singleNumber(vector<int>& nums) { int p=0; for (int i=0; i<nums.size(); i++) { p=p^nums[i]; } //找出p從右向左第一次二進制 1 的位置 int index = findIndexOne(p); //根據當前位置 index 是否為1 ,把數組分為2份 vector<int>vec; vec.push_back(0); vec.push_back(0); for (int i=0; i<nums.size(); i++) { if(isBitOne(nums[i],index)){ vec[0]=vec[0]^nums[i]; } else { vec[1]=vec[1]^nums[i]; } } return vec; } };
數組中只出現一次的兩個數字2
/* 56題-2 數組中只出現一次的兩個數字2 給定一個非空整數數組,除了某個元素只出現一次以外,其余每個元素均出現了三次。找出那個只出現了一次的元素。 */ class class56_2{ int singleNumber(vector<int>& nums) { // [111,111,111,101] = 4 3 4 // 第一步:遍歷數組,對每一個元素的 32位 ,都相加 int lengthTotal = 8 * sizeof(int); vector<int>bits(lengthTotal,0); for (int i=0; i<nums.size(); i++) { int num = nums[i]; int bitMask = 1; for (int j=0; j<lengthTotal; j++) { if( num & bitMask){ bits[j] += 1; } bitMask = bitMask << 1; } } // 對3取余數,不為0 ,就是 單獨數字的對應 位數位1 //從最高位開始,然后 左移一位,相當於乘以2 int index=0; for (int i=lengthTotal-1 ; i>=0; i--) { index = index << 1 ; int v = bits[i]%3; if(v){//有余數 index+=v;//+1 } } return index; } };
和為S的兩個數
/* 57題目 輸入一個遞增排序的數組和一個數字S,在數組中查找兩個數,使得他們的和正好是S, 如果有多對數字的和等於S,輸出任意一對即可 */ class Class_57{ public: vector<int> FindNumbersWithSum(vector<int> array,int sum) { vector<int> result; int len = (int)array.size(); if(len<=1)return result; int Sum; int i = 0; int j = len - 1; while(i<j){ Sum = array[i] + array[j]; if(Sum>sum) j--; else if(Sum<sum) i++; else{ result.push_back(array[i]); result.push_back(array[j]); break; } } return result; } };
和為S的連續正數序列
/* 第57-2題 和為S的連續正數序列 輸入一個正數s,打印出所有和為s的連續正數序列(至少含有兩個數)。例如輸入15,由於1+2+3+4+5=4+5+6=7+8=15;所以打印出三個連續序列1~5,4~6,7~8; */ class Class_57_2{ public: void FindContinuousSequence(int sum) { if(sum < 3) return; int small = 1; int big = 2; int middle = (1 + sum) / 2; int curSum = small + big; while(small < middle)//循環到中間值即可,之后的值的和肯定是大於sum { if(curSum == sum) PrintContinuousSequence(small, big); while(curSum > sum && small < middle) { curSum -= small; small ++; if(curSum == sum) PrintContinuousSequence(small, big); } big ++; curSum += big; } } void PrintContinuousSequence(int small, int big) { for(int i = small; i <= big; ++ i) printf("%d ", i); printf("\n"); } };
翻轉字符串 leetcode(151)
/* 58題:翻轉字符串 leetcode(151) 給定一個字符串,逐個翻轉字符串中的每個單詞。 無空格字符構成一個單詞。 輸入字符串可以在前面或者后面包含多余的空格,但是反轉后的字符不能包括。 如果兩個單詞間有多余的空格,將反轉后單詞間的空格減少到只含一個。 */ class class58{ void reverse2(string& s,int i ,int j){ int begin=i; int end = j; while (begin<end) { char temp = s[begin]; s[begin]=s[end]; s[end]=temp; begin++; end--; } } // abc edf abc string reverseWords(string s) { string finalS = s; reverse2(finalS,0,(int)s.length()-1); string temp=""; string newStr=""; finalS+=' '; for(int i=0;i<finalS.length();i++){ if(finalS[i]==' '){ if(finalS[i+1]==' '){//連續的空格 } else if(temp.length()==0){//字符串開頭就有空格 } else{ //到了單詞的底部了 reverse2(temp, 0, (int)temp.length()-1); newStr+=temp+' '; temp=""; } } else{ temp+=finalS[i]; } } return newStr.length()>1?newStr.substr(0,newStr.length()-1):newStr; } };
左旋轉字符串
/* 58題-2:左旋轉字符串 字符串的左旋轉操作是把字符串前面的若干個字符轉移到字符串的尾部。 請定義一個函數實現字符串坐旋轉操作的功能。比如輸入字符串“abcdefg"和數字2,該函數將返回左旋轉兩位得到的結果:”cdefgab" */ class class58_2 { void reverse2(string& s,int i ,int j){ int begin=i; int end = j; while (begin<end) { char temp = s[begin]; s[begin]=s[end]; s[end]=temp; begin++; end--; } } string LeftRotateString(string s,int n){ int nLength = (int)s.length(); if(nLength > 0 && n > 0 && n < nLength) { int pFirstStart = 0; int pFirstEnd = n - 1; int pSecondStart = n; int pSecondEnd = nLength - 1; reverse2(s,pFirstStart, pFirstEnd); reverse2(s,pSecondStart, pSecondEnd); reverse2(s,pFirstStart, pSecondEnd); } return s; } };
滑動窗口最大值
/* 59題目 滑動窗口最大值 給定一個數組和滑動窗口的大小,請找出所有滑動窗口里的最大值。例如,{2,3,4,2,6,2,5,1}以及窗口大小3,那么存在6個滑動窗口,最大值分別為{4,4,6,6,6,5} 滑動 思路 滑動窗口可以看作是隊列,為了得到滑動窗口的最大值,隊列可以從兩端刪除元素,因此使用雙向隊列。 原則:對於新來的元素k,將其與隊列中元素比較, 1、若隊列中有元素比k小,直接將之前比k小的元素移除隊列(因為不可能再成為后面滑動窗口的最大值),壓入k 2、若隊列中元素x比k大,則根據下標檢測x是否在窗口內,如果不在則移除隊列,壓入k 隊列的第一個元素是滑動窗口的最大值,同時要存儲數字在數組的下標,而不是數值,用來判斷滑動窗口是否包含數字 舉例說明: 1、將2壓入隊列 2、3比2大,彈出2,壓入3 3、4比3大,彈出3,壓入4 4、2比4小,4還在窗口內,保留4,壓入2 5、6比4和2都大,因此彈出4和2,壓入6 6、2比6小,6在窗口內,保留6,壓入2 7、5比2大,比6小,且6還在窗口內,彈出2,壓入5 8、1比6和5都小,但是6不在窗口內,6彈出,壓入1 */ class class59_1 { public: vector<int> maxSlidingWindow(vector<int>& nums, int size) { vector<int>maxInWindows; if(nums.size()>=size && size>=1 ){ deque<int>index; for (int i=0; i<size; i++) { while (index.size()!=0 && nums[i]>= nums[index.back()]) { index.pop_back();//從隊尾出來 } index.push_back(i);//對頭是最大值 } for (int i = size; i<nums.size(); i++) { //繼續向后遍歷 //把上一組的最大值加入到vector maxInWindows.push_back(nums[index.front()]);//對頭放最大值 //繼續尋找下一組的最大值 while (index.size()>0&&nums[i]>= nums[index.back()]) {//當前元素的值大於隊尾,讓他們出列 index.pop_back(); } //當前處理的下標和頭部的下標大於等於 size的時候,對頭的就出列 if(index.size()>0 && i-index.front()>=size){ index.pop_front(); } index.push_back(i); } //把最后一組最大值加入vector maxInWindows.push_back(nums[index.front()]); } return maxInWindows; } };
隊列最大值
/* 題目59-2 請定義一個隊列並實現函數max得到隊列里的最大值,要求函數max,push_back,pop_front的時間復雜度都是O(1) 思路: 和上一題類似, 1新建一個保持最大值的雙向隊列,最大值始終放到隊列頭 2 移除的元素如果是最大值,才需要移除索引隊列 */ class class59_2{ public: class59_2() : currIndex(0) {} void push_back(int number) { while (!maxisium.empty() && number > maxisium.back().number) maxisium.pop_back(); Inner inner = { number, currIndex }; data.push_back(inner); maxisium.push_back(inner); ++currIndex; } void pop_front() { if (data.front().index == maxisium.front().index) maxisium.pop_front(); data.pop_front(); } int max() { return maxisium.front().number; } private: struct Inner { int number; int index; }; deque<Inner> maxisium; deque<Inner> data; int currIndex; };
n個骰子的點數
/*題目60 n個骰子的點數 把n個骰子扔到地上,所有骰子朝上一面的點數之后為s. 輸入n,打印出s所有可能的值出現的概率。(每個骰子6個面,點數從1到6) */ class class_60{ /* 方法一:回溯法:僅供測試,未剪枝 */ //2 3 4 2 6 2 5 1 size = 3 void shaziSub(vector<vector<int>>vecs,int n,int s,int &count,int &curSum,int m){ if(m==n){//最后一個骰子已經算完 if(curSum==s){ count++; } return; } vector<int>curVec = vecs[m];//當前第幾個骰子 for (int i=0; i<6; i++) {//遍歷骰子的6個值 curSum+=curVec[i]; //看下一個骰子 shaziSub(vecs, n, s, count, curSum,m+1); //回溯 curSum-=curVec[i]; } } int shaziDianshu(int n,int s){ vector<vector<int>> vec; for (int i=0; i<n; i++) { vector<int>subVec; for (int j=1; j<=6; j++) { subVec.push_back(j); } vec.push_back(subVec); } int count=0; int curSum=0; int m=0;//第幾個骰子 shaziSub(vec, n, s, count,curSum, m); return count; } /* 方法二: 遞歸 缺點,:很多重復計算 比如 計算和為4,要從1開始加,不能利用之前計算的結果 遞歸的思想一般是分而治之,把n個骰子分為第一個和剩下的n-1個。先計算第一個骰子每個點數出現的次數,再計算剩余n-1個骰子出現的點數之和。求n-1個骰子的點數之的方法和前面講的一樣,即再次把n-1個骰子分成兩堆------第一個和剩下的n-2個。n個骰子,每個骰子6個面,總共有6n個組合。這6n個組合之中肯定有重復的,我們知道其范圍是n~6n,對於每種情況我們可以用緩存機制記錄下來,每當其發生一次我們令其對應的單元加1。 我們定義一個長度為6n-n+1的數組,和為s的點數出現的次數保存到數組第s-n個元素里。為什么是6n-n+1呢?因為n個骰子的和最少是n,最大是6n,介於這兩者之間的每一個情況都可能會發生,總共6n-n+1種情況。 */ int g_maxValue = 6; void Probability(int original, int current, int sum, int* pProbabilities) { if(current == 1) { pProbabilities[sum - original]++; } else { for(int i = 1; i <= g_maxValue; ++i) { Probability(original, current - 1, i + sum, pProbabilities); } } } void Probability(int number, int* pProbabilities) { for(int i = 1; i <= g_maxValue; ++i) Probability(number, number, i, pProbabilities); } void PrintProbability_Solution1(int number) { if(number < 1) return; int maxSum = number * g_maxValue; int* pProbabilities = new int[maxSum - number + 1]; for(int i = number; i <= maxSum; ++i) pProbabilities[i - number] = 0; Probability(number, pProbabilities); int total = pow((double)g_maxValue, number); for(int i = number; i <= maxSum; ++i) { // double ratio = (double)pProbabilities[i - number] / total; double ratio = (double)pProbabilities[i - number]; printf("%d: %f\n", i, ratio); } delete[] pProbabilities; } /* 方法三: 動態規划: 基於循環,時間性能好 遞歸一般是自頂向下的分析求解,而基於循環的方法則是自底向上。 基於循環的一般需要更少的空間和更少的時間,性能較好,但是一般代碼比較難懂。 思路關鍵點: 我們考慮一個骰子一個骰子的放,比如 第一次剖第一個,那么和為1-6 的次數都是1 然后放置第二個骰子, 和的集合為 2-12 ,第二個骰子仍然是6個面,大小為 1 2 3 4 5 6 比如 計算 和為 8 ,可以是 1和f(7), 2和f(6) ,3和f(5),4和f(4),5和f(3), 6和f(2) 也就是上一次所有骰子的f(7) f(6) f(5) f(4) f(3) f(2) 的總和,有點動態規划的意思 */ void PrintProbability_Solution3(int number) { if(number < 1) return; int* pProbabilities[2]; pProbabilities[0] = new int[g_maxValue * number + 1]; pProbabilities[1] = new int[g_maxValue * number + 1]; for(int i = 0; i < g_maxValue * number + 1; ++i) { pProbabilities[0][i] = 0; pProbabilities[1][i] = 0; } int flag = 0; for (int i = 1; i <= g_maxValue; ++i){ pProbabilities[flag][i] = 1; //[0][1] [0][2] [0][3] [0][4] [0][5] [0][6] = 1 } //從第二次開始擲骰子,假設第一個數組中的第n個數字表示骰子和為n出現的次數, //在下一循環中,我們加上一個新骰子,此時和為n的骰子出現次數應該等於上一次循環中骰子點數和為n-1,n-2,n-3,n-4,n-5, //n-6的次數總和,所以我們把另一個數組的第n個數字設為前一個數組對應的n-1,n-2,n-3,n-4,n-5,n-6之和 for (int k = 2; k <= number; ++k)//剩下的骰子 { //第k次擲骰子,和最小為k,小於k的情況是不可能發生的!所以另不可能發生的次數設置為0! for(int i = 0; i < k; ++i){ pProbabilities[1 - flag][i] = 0; //[1][0] [1][1] = 0 } //第k次擲骰子,和最小為k,最大為g_maxValue*k for (int i = k; i <= g_maxValue * k; ++i)// 2 - 12 { //初始化,因為這個數組要重復使用,上一次的值要清0 pProbabilities[1 - flag][i] = 0; for(int j = 1; j <= i && j <= g_maxValue; ++j) { pProbabilities[1 - flag][i] += pProbabilities[flag][i - j]; } } flag = 1 - flag; } double total = pow((double)g_maxValue, number); for(int i = number; i <= g_maxValue * number; ++i) { double ratio = (double)pProbabilities[flag][i]; printf("%d: %f\n", i, ratio); } delete[] pProbabilities[0]; delete[] pProbabilities[1]; } };
撲克牌中的順子
/* 題目:61 撲克牌中的順子 LL今天心情特別好,因為他去買了一副撲克牌,發現里面居然有2個大王,2個小王(一副牌原本是54張_)…他隨機從中抽出了5張牌,想測測自己的手氣,看看能不能抽到順子,如果抽到的話,他決定去買體育彩票,嘿嘿!!“紅心A,黑桃3,小王,大王,方片5”,“Oh My God!”不是順子…LL不高興了,他想了想,決定大\小 王可以看成任何數字,並且A看作1,J為11,Q為12,K為13。上面的5張牌就可以變成“1,2,3,4,5”(大小王分別看作2和4),“So Lucky!”。LL決定去買體育彩票啦。 現在,要求你使用這幅牌模擬上面的過程,然后告訴我們LL的運氣如何, 如果牌能組成順子就輸出true,否則就輸出false。為了方便起見,你可以認為大小王是0。 思路: 1 數組排序 2 統計 0 的個數 3 統計 連續數字之間空缺的個數,如果0>=空缺的個數,就是順子 */ bool IsContinuous( vector<int> numbers ) { if(numbers.size()==0){ return false; } sort(numbers.begin(), numbers.end()); int zeroCount=0; int gapCount=0; //統計0 的個數 for(int i=0;i<numbers.size();i++){ if(numbers[i]==0){ zeroCount++; } } for (int i = zeroCount+1; i<numbers.size(); i++) { //如果兩個相等,說明有對子,不是順子 if(numbers[i]==numbers[i-1]){ return false; } gapCount+= numbers[i-1]-numbers[i]+1; } return gapCount>zeroCount?false:true; }
圓圈中最后剩下的數字
/* 題目62 有個游戲是這樣的:首先,讓小朋友們圍成一個大圈。然后,他隨機指定一個數m,讓編號為0的小朋友開始報數。 每次喊到m-1的那個小朋友要出列並且不再回到圈中,從他的下一個小朋友開始, 繼續0…m-1報數….這樣下去….直到剩下最后一個小朋友,求最后一個小朋友的編號 */ class class62{ int LastRemaining(int n ,int m){//n=5 m=3 if(n<1||m<1){ return -1; } //先組裝 vector<int>vec; for(int i=0;i<n;i++){ vec.push_back(i);//編號 } if(m==1){ return vec[n-1]; } int index=1; auto it = vec.begin(); while (vec.size()>1) { ++index; it++; if(it==vec.end()){ //超出底部了。回歸0 it = vec.begin(); } if(index%m==0){ vec.erase(it);//it特點,刪除之后,it自動指向下一個位置 if(it==vec.end()){//到底之后,重新到頭 it= vec.begin(); } index=1; } } return vec[0]; } };
股票的最大利潤
/* 63題目:股票的最大利潤 股票的最大利潤(一次賣出) 假設把某股票的價格按照時間先后順序存儲在數組中,請問買賣該股票一次可獲得的最大利潤是多少? 例子: 例如,一只股票在某些時間節點的價格為{9,11,8,5,7,12,16,14}。 如果我們能在價格為5的時候買入並在價格為16時賣出,則能獲得最大的利潤為11. */ //動態規划 /* 4個步驟 1 單個問題: 總共有n天,求最大利潤 1天,求最大利潤 2天,求最大利潤 2 邊界值 第一天 利潤為0,最小值為9 3 狀態i 為 截止到i天 ,所獲的的最大利潤 4 方程 f(n) =max(f(n-1),n天的價格-最小值) */ // 9 11 8 5 7 12 16 14 class class_63{ int maxProfit(vector<int>& prices) { int num = (int)prices.size(); if(num==0){ return 0; } vector<int>vec; int min=prices[0]; for (int i =0; i<num; i++) { vec.push_back(0); } for (int i=1; i<num; i++) { int day = prices[i]; if(day<min){ min=day; } vec[i]=max(vec[i-1],day-min); } return vec[num-1]; } };
求1+2+3…+n
/* 64題目:求1+2+3…+n 求1+2+3+…+n,要求不能使用乘除法、for、while、if、else、switch、case等關鍵字及條件判斷語句(A?B:C)。 */ class class_64{ public: int Sum_Solution(int n) { int res = n; //遞歸跳出條件,當res為假時,無需判斷后序語句,返回res res&&(res+=Sum_Solution(n-1)); return res; } };
求兩個整數之和
/* 65題目: 寫一個函數,求兩個整數之和,要求在函數體內不得使用+、-、*、/四則運算符號。 */ class class_65{ int Add(int num1,int num2){ int sum; int carry; do { sum = num1^num2; carry = (num1 & num2) << 1; num1 = sum; num2 = carry; } while (num2!=0); return num1; } };
構建乘積數組
/* 66題目 構建乘積數組 給定一個數組A[0,1,…,n-1],請構建一個數組B[0,1,…,n-1], 其中B中的元素B[i]=A[0]A[1]…*A[i-1]A[i+1]…*A[n-1]。不能使用除法。 */ class class_66{ void BuildProductionArray(const vector<double>& input, vector<double>& output) { int length1 = (int)input.size(); int length2 = (int)output.size(); if(length1 == length2 && length2 > 1) { output[0] = 1; for(int i = 1; i < length1; ++i) { output[i] = output[i - 1] * input[i - 1]; } double temp = 1; for(int i = length1 - 2; i >= 0; --i) { temp *= input[i + 1]; output[i] *= temp; } } } };
最低公共節點
/* 67題目: 最低公共節點: */ class class_67{ struct TreeNode { int val; TreeNode *left; TreeNode *right; TreeNode(int x) : val(x), left(NULL), right(NULL) {} }; /* 變式1 樹是二叉搜索樹 */ TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) { if(!p||!q){ return nullptr; } if(root==nullptr)return nullptr; int v = root->val; int v1 = p->val; int v2 = q->val; if((v>v1&&v<v2)||(v>v2&&v<v1)){ return root; } else if(v>v1&&v>v2){ return lowestCommonAncestor(root->left, p, q); } else if(v<v1&&v<v2){ return lowestCommonAncestor(root->right, p, q); } else if(v==v1||v==v2){ return root; } return nullptr; } /* 變式2 樹是普通的二叉樹(不是二叉樹,而是樹的話也是一個道理,就類似於圖了) */ /*思考: 1.兩個節點的公共祖先一定在從根節點,至這兩個節點的路徑上。 2.由於求公共祖先中的最近公共祖先,那么即同時出現在這兩條路 徑上的離根節點最遠的節點(或離兩個最近)。 3.最終算法即:求p節點路徑,q節點路徑,兩路徑上最后一個相同 的節點。 所以,我們先求跟節點到某節點的路徑的函數(深度搜索) 步驟: 1.從根節點遍歷(搜索)至該節點,找到該節點后就結束搜索。 2.將遍歷過程中遇到的節點按照順序存儲起來,這些節點即路徑節點。 */ /* 參數1:正在遍歷的節點 參數2:待搜索的節點 參數3:遍歷時的節點路徑 參數4:最終搜索到節點search的路徑結果 參數5:記錄是否找到節點search變量,未找到是0,找到是1 */ void preorder22(TreeNode *node,TreeNode *search, vector<TreeNode*>&path,vector<TreeNode*>&result,int &finish) { if(node==NULL||finish){ return; } path.push_back(node); if(node==search){ finish=1; result=path; } preorder22(node->left, search, path, result,finish); preorder22(node->right, search, path, result,finish); path.pop_back();//b結束遍歷node時,將node從棧中彈出 } /* 然后求兩路徑上最后一個相同的節點 步驟: 1.求出較短路徑的長度n。 2.同時遍歷p節點的路徑與q節點的路徑,遍歷n個節點,最后一個發現的相同節點,即最近公共祖先。 */ TreeNode *lowestCommonAncestor2(TreeNode *root,TreeNode*p,TreeNode *q) { vector<TreeNode*>path;//聲明遍歷用的臨時棧 vector<TreeNode*>node_p_path; vector<TreeNode*>node_q_path; int finish=0; //求出p的路徑 preorder22(root, p, path, node_p_path, finish); //清空path,finish,計算q節點路徑 path.clear(); finish=0; preorder22(root, q, path, node_p_path, finish); //較短路徑的長度 int path_len=0; if(node_p_path.size()<node_q_path.size()){ path_len=(int)node_p_path.size(); } else{ path_len=(int)node_q_path.size(); } //同時遍歷根到p,q兩個節點的路徑上的節點 TreeNode * result=0; for (int i=0; i<path_len; i++) { if(node_p_path[i]==node_q_path[i]){ result=node_p_path[i];//找到最近公共祖先 } } return result; } };