劍指Offer算法刷題解答 --基於牛客網C++編譯器

注:首先感謝牛客網提供了這樣一個平臺,讓我在看書刷題之餘能夠刷到書上的題,希望牛客網越辦越好,不忘初心~

黑色表示難度較低不會再寫粉色表示打算三刷紅色表示看答案纔會今後必刷。

 

1、賦值運算符函數

注:略。

 

2、實現Singleton模式

注:略。

 

3、數組中重複的數字

題目描述:

在一個長度爲n的數組裏的所有數字都在0到n-1的範圍內。 數組中某些數字是重複的,但不知道有幾個數字是重複的。也不知道每個數字重複幾次。請找出數組中任意一個重複的數字。 例如,如果輸入長度爲7的數組{2,3,1,0,2,5,3},那麼對應的輸出是第一個重複的數字2。

class Solution {
public:
    // Parameters:
    //        numbers:     an array of integers
    //        length:      the length of array numbers
    //        duplication: (Output) the duplicated number in the array number
    // Return value:       true if the input is valid, and there are some duplications in the array number
    //                     otherwise false
    bool duplicate(int numbers[], int length, int* duplication) {
        
        if(length<=0)
            return false;
        
        for(int i=0;i<length;i++)
        {
            while(numbers[i]!=i)
            {
                if(numbers[numbers[i]]==numbers[i])
                {
                    * duplication = numbers[i];
                    return true;
                }
                swap(numbers[numbers[i]], numbers[i]);
            }
            
        }
        return false;
    }
};

注:可以排序,也可以用hash,本題用的書上的最優方法。

 

4、二維數組中的查找

題目描述:

在一個二維數組中(每個一維數組的長度相同),每一行都按照從左到右遞增的順序排序,每一列都按照從上到下遞增的順序排序。請完成一個函數,輸入這樣的一個二維數組和一個整數,判斷數組中是否含有該整數。

class Solution {
public:
    bool Find(int target, vector<vector<int> > array) {
        
        if(array.size()==0 && array[0].size()==0)
            return false;
        
        int left = 0, right = array[0].size(), up = 0, down = array.size();
        while(left<right && up<down)
        {
            int left_c = left, right_c = right, up_c = up, down_c = down;
            
            while(up_c<down_c)
            {
                int mid = up_c + (down_c - up_c)/2;
                if(array[mid][right-1]<target)
                    up_c = mid + 1;
                else if(array[mid][right-1]>target)
                    down_c = mid;
                else return true;
            }
            up = up_c;
            if(up>=down)
                return false;
            
            while(left_c<right_c)
            {
                int mid = left_c + (right_c - left_c)/2;
                if(array[up][mid]<target)
                    left_c = mid + 1;
                else if(array[up][mid]>target)
                    right_c = mid;
                else return true;
            }
            right = left_c;
        }
        return false;
    }
};

注:由於數組的特性,需要從右上或者左下開始二分查找(劍指offer中是順序查找)。

 

5、替換空格

題目描述:

請實現一個函數,將一個字符串中的每個空格替換成“%20”。例如,當字符串爲We Are Happy.則經過替換之後的字符串爲We%20Are%20Happy。

class Solution {
public:
	void replaceSpace(char *str,int length) {
        
        if(length<0 || *str=='\0')
            return ;
        
        char* p = str;
        int numOfspace = 0, lengthOfstring = 0;
        
        while(*p!='\0')
        {
            if(*p==' ')
                numOfspace++;
            p++;
            lengthOfstring++;
        }
        
        int total_Length = lengthOfstring + 2*numOfspace;
        
        while(total_Length>=0 && total_Length<length)
        {
            if(*(str+lengthOfstring)!=' ')
                *(str+total_Length) = *(str+lengthOfstring);
            else
            {
                *(str+total_Length) = '0';
                *(str+total_Length-1) = '2';
                *(str+total_Length-2) = '%';
                total_Length-=2;
            }
            --total_Length;
            --lengthOfstring;
        }
	}
};

注:從字符串尾部開始替換。

 

6、從尾到頭打印鏈表

題目描述:

輸入一個鏈表,按鏈表值從尾到頭的順序返回一個ArrayList。

/**
*  struct ListNode {
*        int val;
*        struct ListNode *next;
*        ListNode(int x) :
*              val(x), next(NULL) {
*        }
*  };
*/
class Solution {
public:
    vector<int> printListFromTailToHead(ListNode* head) {
        
        vector<int> output;
        stack<ListNode*> s;
        
        while(head)
        {
            s.push(head);
            head = head->next;
        }
        
        while(!s.empty())
        {
            output.push_back(s.top()->val);
            s.pop();
        }
        
        return output;
    }
};

注:使用棧來取到鏈表尾部,然後挨個push入vector。

 

7、重建二叉樹

題目描述:

輸入某二叉樹的前序遍歷和中序遍歷的結果,請重建出該二叉樹。假設輸入的前序遍歷和中序遍歷的結果中都不含重複的數字。例如輸入前序遍歷序列{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.size()==0 || vin.size()==0 || pre.size()!=vin.size())
            return nullptr;
        
        int left_pre = 0;
        
        return recover(pre, vin, left_pre, pre.size(), 0, vin.size());
    }
    
    TreeNode* recover(vector<int> pre,vector<int> vin, int& left_pre, int right_pre, int left_vin, int right_vin)
    {
        if(left_pre>=right_pre || left_vin>=right_vin)
            return nullptr;
        
        TreeNode* root = new TreeNode(pre[left_pre]);
        
        int target = Findtarget(vin, left_vin, pre[left_pre]);
        left_pre++;
        
        root->left = recover(pre, vin, left_pre, pre.size(), left_vin, target);
        root->right = recover(pre, vin, left_pre, pre.size(), target+1, right_vin);
        
        return root;
    }
    int Findtarget(vector<int> vin, int init, int target)
    {
        for(int i=init;i<vin.size();i++)
            if(vin[i]==target)
                return i;
        return -1;
    }
};

注:在前序遍歷中找根節點,在中序遍歷中找左右子樹,然後遞歸即可。

 

8、二叉樹的下一個節點

題目描述:

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

/*
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)
            return nullptr;
        
        if(pNode->right)
        {
            TreeLinkNode* p = pNode->right;
            while(p->left)
                p = p->left;
            return p;
        }
        
        while(pNode->next)
        {
            if(pNode == pNode->next->left)
                return pNode->next;
            pNode = pNode->next;
        }
        return nullptr;
    }
};

注:總共有四種情況,如果它是空,則返回空,如果他有右節點,則輸出右節點的最左節點,如果沒有右節點,則如果他是他父節點的左節點,則輸出父節點,如都不是,則遞歸向上找是父節點的左節點的節點,他就是下一個節點。

 

9、用兩個棧實現隊列

題目描述:

用兩個棧來實現一個隊列,完成隊列的Push和Pop操作。 隊列中的元素爲int類型。

class Solution
{
public:
    void push(int node) {
        
        stack1.push(node);
    }

    int pop() {
        
        while(!stack1.empty())
        {
            stack2.push(stack1.top());
            stack1.pop();
        }
        
        int output = stack2.top();
        stack2.pop();
        
        while(!stack2.empty())
        {
            stack1.push(stack2.top());
            stack2.pop();
        }
        return output;
    }

private:
    stack<int> stack1;
    stack<int> stack2;
};

注:定義兩個棧,來回倒騰就行了,倒騰的最後一個數就彈出做輸出。

 

10、斐波那契數列

題目一描述:斐波那契數列

大家都知道斐波那契數列,現在要求輸入一個整數n,請你輸出斐波那契數列的第n項(從0開始,第0項爲0)。n<=39

class Solution {
public:
    int Fibonacci(int n) {
        
        vector<int> cache(n+1,0);
        
        for(int i=0;i<=n;i++)
        {
            if(i==0)
                cache[i] = 0;
            else if(i==1)
                cache[i] = 1;
            else cache[i] = cache[i-1] + cache[i-2];
        }
        return cache[n];
    }
};

注:簡單的動態規劃題,斐波那契數列是f(0)=0,f(1)=1,f(n)=f(n-1)+f(n-2),可以從i=0到i=n從下至上的考慮。這樣簡單很多。

題目二描述:跳臺階

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

class Solution {
public:
    int jumpFloor(int number) {
        
        vector<int> cache(number+1,0);
        
        for(int i=0;i<=number;i++)
        {
            if(i==0 || i==1)
                cache[i] = 1;
            else cache[i] = cache[i-1] + cache[i-2];
        }
        return cache[number];
    }
};

注:與題目思想一致,是斐波那契數列的一種變體,它這裏f(0)也爲1。

題目三描述:變態跳臺階

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

class Solution {
public:
    int jumpFloorII(int number) {
        
        vector<int> cache(number+1,0);
        
        for(int i=0;i<=number;i++)
        {
            if(i==0 || i==1)
                cache[i] = 1;
            else 
            {
                for(int j=1;j<=i;j++)
                    cache[i] += cache[i-j];
            }
        }
        return cache[number];
    }
};

注:與題目二思想一致,是斐波那契數列的一種變體,只需用for循環模擬即可。

題目四描述:矩形覆蓋

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

class Solution {
public:
    int rectCover(int number) {
        
        if(number == 0)
            return 0;
        
        vector<int> cache(number+1,0);
        
        for(int i=0;i<=number;i++)
        {
            if(i==0 || i==1)
                cache[i] = 1;
            else cache[i] = cache[i-1] + cache[i-2];
        }
        return cache[number];
    }
};

注:與題目二思想一致,是斐波那契數列的一種變體,不同的是當number數爲0時,覆蓋方法數也爲0。

 

11、旋轉數組的最小數字

題目描述:

把一個數組最開始的若干個元素搬到數組的末尾,我們稱之爲數組的旋轉。 輸入一個非減排序的數組的一個旋轉,輸出旋轉數組的最小元素。 例如數組{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 0;
        
        int left = 0, right = rotateArray.size()-1;
        
        while(left<right)
        {
            if (rotateArray[left] < rotateArray[right]) 
                return rotateArray[left];
            int mid = left + (right - left)/2;
            if(rotateArray[mid]>rotateArray[left])
                left = mid + 1;
            else if(rotateArray[mid]<rotateArray[right])
                right = mid;
            else ++left;
        }
        return rotateArray[left];
    }
};

注:利用旋轉數組的性質與二分法,如果left指針小於mid指針就說明左面是連續數組,right指針大於mid就說明右面是連續數組,如果都相等則left自加即可。

 

12、矩陣中的路徑

題目描述:

請設計一個函數,用來判斷在一個矩陣中是否存在一條包含某字符串所有字符的路徑。路徑可以從矩陣中的任意一個格子開始,每一步可以在矩陣中向左,向右,向上,向下移動一個格子。如果一條路徑經過了矩陣中的某一個格子,則之後不能再次進入這個格子。 例如 a b c e s f c s a d e e 這樣的3 X 4 矩陣中包含一條字符串"bcced"的路徑,但是矩陣中不包含"abcb"路徑,因爲字符串的第一個字符b佔據了矩陣中的第一行第二個格子之後,路徑不能再次進入該格子。

class Solution {
public:
    bool hasPath(char* matrix, int rows, int cols, char* str)
    {
        if(rows * cols<=0 || *matrix == '\0')
            return false;
        vector<int> cache(rows * cols, 0);
        
        for(int i =0;i<rows;i++)
            for(int j=0;j<cols;j++)
                if(Path(matrix, cache, i, j, rows, cols, str))
                    return true;
        
        return false;
    }
    
    bool Path(char* matrix, vector<int> cache, int i, int j, int rows, int cols, char* str)
    {
        if(*str=='\0')
            return true;
        
        if(*(matrix+i*cols+j)==*str && cache[i*cols+j]==0)
        {
            cache[i*cols+j] = 1;
            
            if(i+1<rows && Path(matrix, cache, i+1, j, rows, cols, str+1))
                return true;
            
            if(j+1<cols && Path(matrix, cache, i, j+1, rows, cols, str+1))
                return true;
            
            if(i-1>=0 && Path(matrix, cache, i-1, j, rows, cols, str+1))
                return true;
            
            if(j-1>=0 && Path(matrix, cache, i, j-1, rows, cols, str+1))
                return true;
            
            if(*(str+1)=='\0')
                return true;
            
            cache[i*cols+j] = 0;
        }
        return false;
    }
};

注:在所有路徑當中挨個遞歸查詢就好了。

 

13、機器人的運動範圍

題目描述:

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

class Solution {
public:
    int movingCount(int threshold, int rows, int cols)
    {
        
        if(rows*cols == 0)
            return 0;
        
        vector<vector<int> > cache(rows,vector<int>(cols, 0));
        
        return PathCount(cache, threshold, 0, 0, rows, cols);
        
    }
    
    int PathCount(vector<vector<int> > &cache, int threshold, int row, int col, int rows, int cols)
    {
        if(row>=rows || col>=cols || row<0 || col<0 || cache[row][col]==1 || cumsum(row)+cumsum(col)>threshold)
            return 0;
        cache[row][col] = 1;
        
        return 1 + 
            PathCount(cache, threshold, row-1, col, rows, cols) + 
            PathCount(cache, threshold, row+1, col, rows, cols) + 
            PathCount(cache, threshold, row, col-1, rows, cols) + 
            PathCount(cache, threshold, row, col+1, rows, cols);
    }
    
    int cumsum(int cache)
    {
        int sum = 0;
        while(cache)
        {
            sum += cache%10;
            cache/=10;
        }
        return sum;
    }
};

注:跟12題一樣的思路,不過增加了額外的限制條件。

 

14、剪繩子

注:略。

 

15、二進制中1的個數

題目描述:

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

class Solution {
public:
     int  NumberOf1(int n) {
         
         int flag = 1, sum = 0;
         
         while(flag)
         {
             if(flag & n)
                 sum++;
             flag = flag<<1;
         }
         
         return sum;
     }
};

注:由於存在負數,所以只能通過左移flag來實現按位與;還有一種方法就是n=(n-1)&n,把一個整數減去1以後再和原來的整數做位與運算,得到的結果相當於把整數的二進制表示中的最右面的1變成0。

 

16、數值的整數次方

題目描述:

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

class Solution {
public:
    double Power(double base, int exponent) {
        
        if(exponent == 0)
            return 1;
        
        if(exponent == 1)
            return base;
        
        if(exponent == -1)
            return 1/base;
        
        double result = Power(base, exponent/2);
        
        if(exponent%2)
        {
            if(exponent<0)
                return result * result * 1/base;
            else return result * result * base;
        }
        else return result * result;
    }
};

注:遞歸二分求解的思想,需要考慮指數爲0和負數的特殊情況。

 

17、打印從1到最大的n位數

 

注:略。

 

18、刪除鏈表中重複的結點

題目描述:

在一個排序的鏈表中,存在重複的結點,請刪除該鏈表中重複的結點,重複的結點不保留,返回鏈表頭指針。 例如,鏈表1->2->3->3->4->4->5 處理後爲 1->2->5

/*
struct ListNode {
    int val;
    struct ListNode *next;
    ListNode(int x) :
        val(x), next(NULL) {
    }
};
*/
class Solution {
public:
    ListNode* deleteDuplication(ListNode* pHead)
    {
        ListNode* m = new ListNode(0);
        m->next = pHead;
        ListNode* p = m;
        ListNode* q = pHead;
        
        while(q && q->next)
        {
            if(q->val == q->next->val)
                q = q->next;
            else
            {
                if(p->next == q)
                    p = p->next;
                else
                    p->next = q->next;
                q = q->next;
            }
        }
        if(p->next!=q)
            p->next = nullptr;
        
        return m->next;
    }
};

注:找規律的鏈表題。

 

19、正則表達式匹配

題目描述:

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

class Solution {
public:
    bool match(char* str, char* pattern)
    {
        if(*str == '\0' && *pattern== '\0')
            return true;
        
        if(*pattern!= '\0' && *(pattern+1) == '*')
        {
            if(*str != '\0' && (*str == *pattern || *pattern=='.'))
                return match(str+1, pattern+2) || match(str, pattern+2) || match(str+1, pattern);
            else return match(str, pattern+2);
        }
        
        if(*str != '\0' && (*str == *pattern || *pattern=='.'))
            return match(str+1, pattern+1);
        return false;
    }
};

注:這種題首先就要考慮特殊條件,即下一個字符爲*時的情況,隨後遞歸各條件即可。

 

20、表示數值的字符串

題目描述:

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

class Solution {
public:
    bool isNumeric(char* string)
    {
        
        if(!string)
            return false;
        
        isUnsignedNumber(&string);
        
        if(*string == '.')
            ++string;
        
        isNumber(&string);
        
        char* p = string;
        if(*string == 'E' || *string == 'e')
        {
            ++string;
            isUnsignedNumber(&string);
        }
        
        if(*string=='\0' && (*p=='\0' || *(p+1)!='\0'))
            return true;
        return false;
    }
    
    void isNumber(char** string)
    {
        while(**string>='0' && **string<='9')
            ++(*string);
    }
    
    void isUnsignedNumber(char** string)
    {
        if(**string=='+' || **string=='-')
            ++(*string);
        isNumber(string);
    }
};

注:這裏唯一需要注意的就是使用指針的指針。

 

21、調整數組順序使奇數位於偶數前面

題目描述:

輸入一個整數數組,實現一個函數來調整該數組中數字的順序,使得所有的奇數位於數組的前半部分,所有的偶數位於數組的後半部分,並保證奇數和奇數,偶數和偶數之間的相對位置不變。

class Solution {
public:
    void reOrderArray(vector<int> &array) {
        
        vector<int> cache(array.size(), 0);
        
        int left = 0;
        
        for(int i=0;i<array.size();i++)
            if(array[i]%2)
                cache[left++] = array[i];
        
        for(int i=0;i<array.size();i++)
            if(!(array[i]%2))
                cache[left++] = array[i];
        
        array = cache;
    }
};

注:如果不要求相對順序不變則可以使用雙指針,但是題目要求相對順序不變,定義一個額外數組,遍歷兩遍原數組即可。

 

22、鏈表中倒數第k個結點

題目描述:

輸入一個鏈表,輸出該鏈表中倒數第k個結點。

/*
struct ListNode {
	int val;
	struct ListNode *next;
	ListNode(int x) :
			val(x), next(NULL) {
	}
};*/
class Solution {
public:
    ListNode* FindKthToTail(ListNode* pListHead, unsigned int k) {
        
        if(k<=0 || !pListHead)
            return nullptr;
        
        ListNode* slow = pListHead, *fast = pListHead;
        
        while(k)
        {
            if(!fast)
                return nullptr;
            fast = fast->next;
            --k;
        }
        
        while(fast)
        {
            slow = slow->next;
            fast = fast->next;
        }
        return slow;
    }
};

注:使用快慢指針,快的先比慢的走k步,然後再一起一步一步的走,這裏需要注意k有可能是非法值,有可能小於等於0,或者直接就大於整個鏈表長度,這時需要返回nullptr指針。

 

23、鏈表中環的入口結點

題目描述:

給一個鏈表,若其中包含環,請找出該鏈表的環的入口結點,否則,輸出null。

/*
struct ListNode {
    int val;
    struct ListNode *next;
    ListNode(int x) :
        val(x), next(NULL) {
    }
};
*/
class Solution {
public:
    ListNode* EntryNodeOfLoop(ListNode* pHead)
    {
        ListNode* m = HasLoop(pHead);
        ListNode* slow = pHead, *fast = pHead;
        if(!m)
            return nullptr;
        
        ListNode* p = m;
        int num = 1;
        while(p->next != m)
        {
            ++num;
            p = p->next;
        }
        
        while(num)
        {
            fast = fast->next;
            --num;
        }
        
        while(slow != fast)
        {
            slow = slow->next;
            fast = fast->next;
        }
        
        return slow;
    }
    
    ListNode* HasLoop(ListNode* pHead)
    {
        if(!pHead)
            return nullptr;
        
        ListNode* slow = pHead, *fast = pHead;
        
        while(fast && fast->next)
        {
            slow = slow->next;
            fast = fast->next->next;
            if(fast == slow)
                return slow;
        }
        return nullptr;
    }
};

注:首先使用快慢(快的步長是慢的二倍)指針判斷有沒有環,如果有快慢指針一定會相交,其次找到相交點計算環的長度,然後根據環的長度再次使用快慢指針(快指針比慢指針先走了環的長度大小,步長均爲1)即可找到環的入口。

 

24、反轉鏈表

題目描述:

輸入一個鏈表,反轉鏈表後,輸出新鏈表的表頭。

/*
struct ListNode {
	int val;
	struct ListNode *next;
	ListNode(int x) :
			val(x), next(NULL) {
	}
};*/
class Solution {
public:
    ListNode* ReverseList(ListNode* pHead) {
        
        ListNode* p = nullptr;
        ListNode* q = pHead;
        
        while(q)
        {
            ListNode* r = q->next;
            q->next = p;
            p = q;
            q = r;
        }
        return p;
    }
};

注:定義p、q、r三個指針,從頭結點還是反轉每一個結點即可。

 

25、合併兩個排序的鏈表

題目描述:

輸入兩個單調遞增的鏈表,輸出兩個鏈表合成後的鏈表,當然我們需要合成後的鏈表滿足單調不減規則。

/*
struct ListNode {
	int val;
	struct ListNode *next;
	ListNode(int x) :
			val(x), next(NULL) {
	}
};*/
class Solution {
public:
    ListNode* Merge(ListNode* pHead1, ListNode* pHead2)
    {
        
        ListNode* m = new ListNode(0);
        ListNode* p = m;
        while(pHead1 || pHead2)
        {
            int num1 = INT_MAX, num2 = INT_MAX;
            
            if(pHead1)
                num1 = pHead1->val;
            
            if(pHead2)
                num2 = pHead2->val;
            
            p->next = new ListNode(min(num1, num2));
            p = p->next;
            
            if(num1>num2)
                pHead2 = pHead2->next;
            else pHead1 = pHead1->next;
        }
        
        return m->next;
    }
};

注:只需要挨個比對鏈表頭結點就好了,我這裏的代碼是使用了哨兵的思想,但是會有一個隱藏bug,當這兩個節點的尾指針全都等於哨兵INT_MAX或者pHead2等於INT_MAX時我的程序就會報錯。

 

26、樹的子結構

題目描述:

輸入兩棵二叉樹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)
    {
        if(!pRoot1 || !pRoot2)
            return false;
        
        if(IsSubtree(pRoot1, pRoot2))
            return true;
        
        return HasSubtree(pRoot1->left, pRoot2) || HasSubtree(pRoot1->right, pRoot2);
    }
    
    bool IsSubtree(TreeNode* pRoot1, TreeNode* pRoot2)
    {
        if(!pRoot2)
            return true;
        
        if(!pRoot1)
            return false;
        
        if(pRoot1->val == pRoot2->val)
            return IsSubtree(pRoot1->left, pRoot2->left) && IsSubtree(pRoot1->right, pRoot2->right);
        return false;
    }
};

注:遞歸判斷各子樹有沒有這個結構就好了。

 

27、二叉樹的鏡像

題目描述:

操作給定的二叉樹,將其變換爲源二叉樹的鏡像。

/*
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)
            return ;
        
        TreeNode *temp = pRoot->left;
        pRoot->left = pRoot->right;
        pRoot->right = temp;
        
        Mirror(pRoot->left);
        Mirror(pRoot->right);
    }
};

注:遞歸交換左右子樹就好了。

 

28、對稱的二叉樹

題目描述:

請實現一個函數,用來判斷一顆二叉樹是不是對稱的。注意,如果一個二叉樹同此二叉樹的鏡像是同樣的,定義其爲對稱的。

/*
struct TreeNode {
    int val;
    struct TreeNode *left;
    struct TreeNode *right;
    TreeNode(int x) :
            val(x), left(NULL), right(NULL) {
    }
};
*/
class Solution {
public:
    bool isSymmetrical(TreeNode* pRoot)
    {
        if(!pRoot)
            return true;
        
        return isSymmetrical(pRoot->left, pRoot->right);
    }
    
    bool isSymmetrical(TreeNode* pRoot1, TreeNode* pRoot2)
    {
        if(!pRoot1 && !pRoot2)
            return true;
        
        if(!pRoot1 || !pRoot2)
            return false;
        
        if(pRoot1->val == pRoot2->val)
            return isSymmetrical(pRoot1->left, pRoot2->right) && isSymmetrical(pRoot1->right, pRoot2->left);
        
        return false;
    }
};

注:遞歸判斷兩個子樹,左子樹等於右子樹,右子樹等於左子樹即可。

 

29、順時針打印矩陣

題目描述:

輸入一個矩陣,按照從外向裏以順時針的順序依次打印出每一個數字,例如,如果輸入如下4 X 4矩陣: 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) {
        
        if(matrix.size()==0 || matrix[0].size()==0)
            return {};
        
        vector<int> output;
        int left = 0, right = matrix[0].size()-1, up = 0, down = matrix.size()-1;
        
        while(left<=right && up<=down)
        {
            int left_c = left, right_c = right, up_c = up, down_c = down;
            
            while(left_c<=right)
                output.push_back(matrix[up][left_c++]);
            up++;
            up_c++;
            
            while(up_c<=down)
                output.push_back(matrix[up_c++][right]);
            right--;
            right_c--;
            
            while(left<=right_c && up<=down)
                output.push_back(matrix[down][right_c--]);
            down--;
            down_c--;
            
            while(up<=down_c && left<=right)
                output.push_back(matrix[down_c--][left]);
            left++;
        }
        return output;
    }
};

注:主要注意一下從右至左以及從下至上打印的邊界條件就好了。

 

30、包含min函數的棧

題目描述:

定義棧的數據結構,請在該類型中實現一個能夠得到棧中所含最小元素的min函數(時間複雜度應爲O(1))。

class Solution {
private:
    stack<int> s,m;
    int minimum = INT_MAX;
public:
    void push(int value) {
        
        s.push(value);
        if(m.empty())
            minimum = value;
        else 
            minimum = ::min(value, minimum);
        m.push(minimum);
    }
    void pop() {
        
        s.pop();
        m.pop();
        minimum = m.top();
    }
    int top() {
        return s.top();
    }
    int min() {
        return minimum;
    }
};

注:需要額外定義一個最小棧,每次壓入彈出棧的時候都需要對最小棧進行操作以取得當前最小值。

 

31、棧的壓入、彈出序列

題目描述:

輸入兩個整數序列,第一個序列表示棧的壓入順序,請判斷第二個序列是否可能爲該棧的彈出順序。假設壓入棧的所有數字均不相等。例如序列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) {
        
        if(pushV.size() == 0 || popV.size() == 0)
            return true;
        
        int pushV_ptr = 0, popV_ptr = 0;
        stack<int> s;
        while(pushV_ptr<pushV.size() && popV_ptr<popV.size())
        {
            while(pushV_ptr<pushV.size() && (s.empty() || s.top()!= popV[popV_ptr]))
                s.push(pushV[pushV_ptr++]);
            
            while(popV_ptr<popV.size() && !s.empty() && s.top()== popV[popV_ptr])
            {
                s.pop();
                popV_ptr++;
            }
        }
        
        if(s.empty())
            return true;
        return false;
    }
};

注:使用一個棧來模擬壓入彈出即可。

 

32、從上往下打印二叉樹

題目一描述:從上往下打印二叉樹

從上往下打印出二叉樹的每個節點,同層節點從左至右打印

/*
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) {
        
        if(!root)
            return {};
        vector<int> output;
        queue<TreeNode*> q;
        q.push(root);
        
        while(!q.empty())
        {
            int size = q.size();
            while(size--)
            {
                TreeNode* cache = q.front();
                output.push_back(cache->val);
                
                if(cache->left)
                    q.push(cache->left);
                if(cache->right)
                    q.push(cache->right);
                q.pop();
            }
        }
        return output;
    }
};

注:使用隊列進行層序遍歷即可。

題目二描述:按之字形順序打印二叉樹

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

/*
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> > Print(TreeNode* pRoot) {
        
        if(!pRoot)
            return {};
        
        vector<vector<int> > output;
        vector<int> cache;
        
        stack<TreeNode*> s1,s2;
        s1.push(pRoot);
        bool flag = true;
        
        while(!s1.empty() || !s2.empty())
        {
            if(flag)
            {
                int size = s1.size();
                while(size--)
                {
                    TreeNode* p = s1.top();
                    cache.push_back(p->val);
                    
                    if(p->left)
                        s2.push(p->left);
                    if(p->right)
                        s2.push(p->right);
                    s1.pop();
                }
                flag = !flag;
            }
            else
            {
                int size = s2.size();
                while(size--)
                {
                    TreeNode* p = s2.top();
                    cache.push_back(p->val);
                    
                    if(p->right)
                        s1.push(p->right);
                    if(p->left)
                        s1.push(p->left);
                    s2.pop();
                }
                flag = !flag;
            }
            output.push_back(cache);
            cache.clear();
        }
        return output;
    }
};

注:使用兩個棧即可,一個先壓入左子樹,一個先壓入右子樹。

 

33、二叉搜索樹的後序遍歷序列

題目描述:

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

class Solution {
public:
    bool VerifySquenceOfBST(vector<int> sequence) {
        
        if(sequence.size()==0)
            return false;
        
        int left = 0, right = sequence.size()-1;
        
        return Verify(sequence, left, right);
        
    }
    
    bool Verify(vector<int> &sequence, int left, int right)
    {
        if(left>=right)
            return true;
        
        int major = sequence[right];
        int mid = right;
        for(int i=left;i<right;i++)
        {
            if(sequence[i]>major)
            {
                mid = i;
                break;
            }
        }
        
        for(int i=mid;i<right;i++)
            if(sequence[i]<major)
                return false;
        
        return Verify(sequence, left, mid-1) && Verify(sequence, mid, right-1);
    }
};

注:根據二叉搜索樹的性質,從後往前找根節點,找到根節點後找左右子樹,分別驗證左右子樹是不是二叉搜索樹(即左子樹都比根節點小,右子樹都比根節點大)即可。

 

34、二叉樹中和爲某一值的路徑

題目描述:

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

/*
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> > output;
        vector<int> cache;
        
        Path(output, root, cache, expectNumber);
        sort(output.begin(),output.end(), setting);
        return output;
    }
    
    void Path(vector<vector<int> >& output, TreeNode* root, vector<int> cache, int expectNumber)
    {
        if(!root)
            return ;
        
        expectNumber-=root->val;
        cache.push_back(root->val);
        
        if(!root->left && !root->right && expectNumber==0)
        {
            output.push_back(cache);
            return ;
        }
        
        Path(output, root->left, cache, expectNumber);
        Path(output, root->right, cache, expectNumber);
    }
    
    static bool setting(vector<int> a, vector<int> b)
    {
        return a.size()>b.size();
    }
};

注:挨個路徑去驗證就好了,驗證完還需要對數組長度進行排序才能輸出。

 

35、複雜鏈表的複製

題目描述:

輸入一個複雜鏈表(每個節點中有節點值,以及兩個指針,一個指向下一個節點,另一個特殊指針指向任意一個節點),返回結果爲複製後複雜鏈表的head。(注意,輸出結果中請不要返回參數中的節點引用,否則判題程序會直接返回空)

/*
struct RandomListNode {
    int label;
    struct RandomListNode *next, *random;
    RandomListNode(int x) :
            label(x), next(NULL), random(NULL) {
    }
};
*/
class Solution {
public:
    RandomListNode* Clone(RandomListNode* pHead)
    {
        if(!pHead)
            return nullptr;
        
        RandomListNode* m = new RandomListNode(0);
        m->next = pHead;
        
        while(pHead)
        {
            RandomListNode* p = new RandomListNode(pHead->label);
            p->next = pHead->next;
            pHead->next = p;
            pHead = p->next;
        }
        
        RandomListNode* cache = m->next;
        while(cache)
        {
            if(cache->random)
                cache->next->random = cache->random->next;
            cache = cache->next->next;
        }
        
        RandomListNode* cache_2 = m;
        RandomListNode* cache_3 = m->next;
        while(cache_2->next)
        {
            cache_2->next = cache_2->next->next;
            cache_2 = cache_2->next;
            
            cache_3->next = cache_3->next->next;
            cache_3 = cache_3->next;
        }
        
        return m->next;
    }
};

注:本題分三步走,第一步是複製鏈表,複製每一個結點爲它的下一結點,例如ABCD就複製爲AABBCCDD,第二步是複製random結點,第三步是斷開連接(這裏連接要全部斷開,否則要報錯)。

 

36、二叉搜索樹與雙向鏈表

題目描述:

輸入一棵二叉搜索樹,將該二叉搜索樹轉換成一個排序的雙向鏈表。要求不能創建任何新的結點,只能調整樹中結點指針的指向。

/*
struct TreeNode {
	int val;
	struct TreeNode *left;
	struct TreeNode *right;
	TreeNode(int x) :
			val(x), left(NULL), right(NULL) {
	}
};*/
class Solution {
public:
    TreeNode* Convert(TreeNode* pRootOfTree)
    {
        if(!pRootOfTree)
            return nullptr;
        
        TreeNode* temp = Con(pRootOfTree, pRootOfTree);
        while(temp->left)
            temp = temp->left;
        return temp;
    }
    
    TreeNode* Con(TreeNode* pRootOfTree, TreeNode* cur)
    {
        if(!pRootOfTree)
            return nullptr;
        
        TreeNode* temp = Con(pRootOfTree->left,pRootOfTree);
        pRootOfTree->left = temp;
        if(temp)
            temp->right = pRootOfTree;
        
        temp = Con(pRootOfTree->right,pRootOfTree);
        if(temp)
            temp->left = pRootOfTree;
        pRootOfTree->right = temp;
        
        
        if(pRootOfTree == cur->right)
            while(pRootOfTree->left)
                pRootOfTree = pRootOfTree->left;
        else
            while(pRootOfTree->right)
                pRootOfTree = pRootOfTree->right;
        
        return pRootOfTree;
    }
};

注:其實就是中序遍歷整個樹,每次把它左子樹的最右節點的right=root,它左子樹的最左節點的left=root,這裏需要注意的就是,當這個子樹是父節點的左子樹時它需要遞歸到最右節點,當這個子樹是父節點的右子樹時它需要遞歸到最左節點,所以就有了如上代碼。

 

37、序列化二叉樹

題目描述:

請實現兩個函數,分別用來序列化和反序列化二叉樹

/*
struct TreeNode {
    int val;
    struct TreeNode *left;
    struct TreeNode *right;
    TreeNode(int x) :
            val(x), left(NULL), right(NULL) {
    }
};
*/
class Solution {
public:
    char* Serialize(TreeNode *root) {
        
        if(!root)
            return nullptr;
        
        string s = "";
        Encode(root, s);
        
        char *str = new char[s.size()+1];
        strcpy(str, s.c_str());
        return str;
    }
    
    void Encode(TreeNode *root, string &s)
    {
        if(!root)
        {
            s += "#,";
            return;
        }
        
        s += to_string(root->val);
        s.push_back(',');
        
        Encode(root->left, s);
        Encode(root->right, s);
    }
    
    TreeNode* Deserialize(char *str) {
        
        if(!str)
            return nullptr;
        
        string s(str);
        return  Decode(s);
    }
    
    TreeNode* Decode(string &s)
    {
        if(s.size()==0)
            return nullptr;
        
        if(s[0]=='#')
        {
            s = s.substr(2);
            return nullptr;
        }
        
        TreeNode* root = new TreeNode(stoi(s));
        
        s = s.substr(s.find_first_of(',')+1);
        
        root->left = Decode(s);
        root->right = Decode(s);
        
        return root;
    }
};

注:https://blog.csdn.net/u011475210/article/details/78889876

 

38、字符串的排列

題目描述:

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

輸入描述:

輸入一個字符串,長度不超過9(可能有字符重複),字符只包括大小寫字母。
class Solution {
public:
    vector<string> Permutation(string str) {
        
        if(str.size()==0)
            return {};
        
        vector<string> output;
        sort(str.begin(), str.end());
        Permutation(output, str, 0);
        return output;
    }
    
    void Permutation(vector<string> &output, string str, int k)
    {
        if(k == str.size())
        {
            output.push_back(str);
            return ;
        }
        
        for(int i=k;i<str.size();i++)
        {
            if((i!=k && str[i]==str[k]) || (i!=k && str[i]==str[i-1]))
               continue;
            swap(str[i], str[k]);
            Permutation(output, str, k+1);
        }
    }
};

注:將每個字符分別與第一個字符交換,然後遞歸即可,同時需要考慮去重。

 

39、數組中出現次數超過一半的數字

題目描述:

數組中有一個數字出現的次數超過數組長度的一半,請找出這個數字。例如輸入一個長度爲9的數組{1,2,3,2,2,2,5,4,2}。由於數字2在數組中出現了5次,超過數組長度的一半,因此輸出2。如果不存在則輸出0。

class Solution {
public:
    int MoreThanHalfNum_Solution(vector<int> numbers) {
        
        if(numbers.size()==0)
            return 0;
        
        int cache = numbers[0],num = 1;
        
        for(int i=1;i<numbers.size();i++)
        {
            if(numbers[i] == cache)
                ++num;
            else 
            {
                --num;
                if(num == 0)
                {
                    cache = numbers[i];
                    num = 1;
                }
            }
        }
        int times = 0;
        
        for(int i=0;i<numbers.size();i++)
            if(numbers[i]==cache)
                times++;
        
        return times>numbers.size()/2?cache:0;
    }
};

注:本題如果允許用額外空間可以使用hash表,但是最優解是空間複雜度爲O(1),時間複雜度爲O(n)。

 

40、最小的K個數

題目描述:

輸入n個整數,找出其中最小的K個數。例如輸入4,5,1,6,2,7,3,8這8個數字,則最小的4個數字是1,2,3,4,。

class Solution {
public:
    vector<int> GetLeastNumbers_Solution(vector<int> input, int k) {
        
        if(input.size()==0 || input.size()<k || k<=0)
            return {};
        
        vector<int> output;
        
        int index = FindPrivot(input, 0, input.size()-1);
        while(index != k-1)
        {
            if(index<k-1)
                index = FindPrivot(input, index+1, input.size()-1);
            else
                index = FindPrivot(input, 0, index-1);
        }
        
        for(int i=0;i<k;i++)
            output.push_back(input[i]);
        
        return output;
    }
    
    int FindPrivot(vector<int> &input, int left, int right)
    {
        if(left == right)
            return left;
        
        int privot = input[right], index = left;
        
        for(int i=left;i<right;i++)
        {
            if(input[i]<=privot)
            {
                swap(input[i], input[index]);
                index++;
            }
        }
        
        swap(input[right], input[index]);
        
        return index;
    }
};

注:利用快速排序中patition的思想,通過尋找主元的位置來確定前K個數。

 

41、數據流中的中位數

題目描述:

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

class Solution {
private:
    vector<int> maximum, minimum;
    
public:
    void Insert(int num)
    {
        int sum = maximum.size() + minimum.size();
        
        if(sum&1)
        {
            if(minimum.size()>0 && num>minimum[0])
            {
                minimum.push_back(num);
                push_heap(minimum.begin(), minimum.end(), greater<int>());
                
                num = minimum[0];
                
                pop_heap(minimum.begin(), minimum.end(), greater<int>());
                minimum.pop_back();
            }
            maximum.push_back(num);
            push_heap(maximum.begin(), maximum.end(), less<int>());
        }
        else
        {
            if(maximum.size()>0 && num<maximum[0])
            {
                maximum.push_back(num);
                push_heap(maximum.begin(), maximum.end(), less<int>());
                
                num = maximum[0];
                
                pop_heap(maximum.begin(), maximum.end(), less<int>());
                maximum.push_back(num);
            }
            minimum.push_back(num);
            push_heap(minimum.begin(), minimum.end(), greater<int>());
        }
    }
    
    double GetMedian()
    { 
        int sum = maximum.size() + minimum.size();
        
        if(sum&1)
            return double(minimum[0]);
        else return double((minimum[0]+maximum[0]))/2;
    }

};

注:首先要保證數據平均分配到兩個堆中,因此兩個堆中的數據的數目之差不能爲1。爲了實現平均分配,可以在數據的總數目是偶數的情況下把新數據插入最小堆,否則插入最大堆。還要保證最大堆的所有數都大於最小堆,因此按照前面的分配規則,會把新的數據插入最小堆。如果此時插入最小堆的數據大於最大堆的最小值,那麼它就不能成爲最小堆的數據,因此我們需要把這個新數據先插入最大堆,然後取出最大堆裏最小值,把最小值插入最小堆中,以此來滿足我們的規則——最小堆中所有數字都大於最大堆的數字。同理可插入新數據進入最大堆。
 

42、連續子數組的最大和

題目描述:

HZ偶爾會拿些專業問題來忽悠那些非計算機專業的同學。今天測試組開完會後,他又發話了:在古老的一維模式識別中,常常需要計算連續子向量的最大和,當向量全爲正數的時候,問題很好解決。但是,如果向量中包含負數,是否應該包含某個負數,並期望旁邊的正數會彌補它呢?例如:{6,-3,-2,7,-15,1,2,2},連續子向量的最大和爲8(從第0個開始,到第3個爲止)。給一個數組,返回它的最大連續子序列的和,你會不會被他忽悠住?(子向量的長度至少是1)

class Solution {
public:
    int FindGreatestSumOfSubArray(vector<int> array) {
        
        if(array.size()==0)
            return 0;
        
        int maximum = INT_MIN, temp = 0;
        
        for(int i=0;i<array.size();i++)
        {
            temp += array[i];
            maximum = max(maximum, temp);
            if(temp<0)
                temp = 0;
        }
        return maximum;
    }
};

注:使用temp記錄增量和,當temp<0時代表無論之後的序列爲何值,加上之前的序列都會變小,所以當temp<0時令temp=0。

 

43、整數中1出現的次數(從1到n整數中1出現的次數)

題目描述:

求出1~13的整數中1出現的次數,並算出100~1300的整數中1出現的次數?爲此他特別數了一下1~13中包含1的數字有1、10、11、12、13因此共出現6次,但是對於後面問題他就沒轍了。ACMer希望你們幫幫他,並把問題更加普遍化,可以很快的求出任意非負整數區間中1出現的次數(從1 到 n 中1出現的次數)。

 class Solution {
    public:
     int NumberOf1Between1AndN_Solution(int n) {
        
        int count = 0;//1的個數
        int i = 1;//當前位
        int current = 0,after = 0,before = 0;
        while((n/i)!= 0){           
            current = (n/i)%10; //高位數字
            before = n/(i*10); //當前位數字
            after = n-(n/i)*i; //低位數字
            //如果爲0,出現1的次數由高位決定,等於高位數字 * 當前位數
            if (current == 0)
                count += before*i;
            //如果爲1,出現1的次數由高位和低位決定,高位*當前位+低位+1
            else if(current == 1)
                count += before * i + after + 1;
            //如果大於1,出現1的次數由高位決定,//(高位數字+1)* 當前位數
            else{
                count += (before + 1) * i;
            }    
            //前移一位
            i = i*10;
        }
        return count;
    }
};

注: 設N = abcde ,其中abcde分別爲十進制中各位上的數字。 如果要計算百位上1出現的次數,它要受到3方面的影響:百位上的數字,百位以下(低位)的數字,百位以上(高位)的數字。 ① 如果百位上數字爲0,百位上可能出現1的次數由更高位決定。比如:12013,則可以知道百位出現1的情況可能是:100~199,1100~1199,2100~2199,,...,11100~11199,一共1200個。可以看出是由更高位數字(12)決定,並且等於更高位數字(12)乘以 當前位數(100)。 ② 如果百位上數字爲1,百位上可能出現1的次數不僅受更高位影響還受低位影響。比如:12113,則可以知道百位受高位影響出現的情況是:100~199,1100~1199,2100~2199,,....,11100~11199,一共1200個。和上面情況一樣,並且等於更高位數字(12)乘以 當前位數(100)。但同時它還受低位影響,百位出現1的情況是:12100~12113,一共114個,等於低位數字(113)+1。 ③ 如果百位上數字大於1(2~9),則百位上出現1的情況僅由更高位決定,比如12213,則百位出現1的情況是:100~199,1100~1199,2100~2199,...,11100~11199,12100~12199,一共有1300個,並且等於更高位數字+1(12+1)乘以當前位數(100)。

https://www.nowcoder.com/profile/3371548/codeBookDetail?submissionId=16319486

 

44、數字序列中的某一位的數字

題目描述:Leetcode:400. 第N個數字

在無限的整數序列 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, ...中找到第 個數字。

class Solution {
public:
    int findNthDigit(int n) {
        
        if(n<10)
            return n;
        
        long base = 1, multi = 1;

        while(n>9*base*multi)
        {
            n -= 9*base*multi;
            base *= 10;
            ++multi;
        }
        
        base/=10;
        
        int firstnum = pow(10, multi-1);
        int ptr = n/multi;
        int rest = n%multi;

        int num = firstnum + ptr - 1;
        
        if(rest)
        {
            ++num;
            for(int i=0;i<multi-rest;i++)
                num/=10;
        }
        return num%10;
    }
};

注:https://blog.csdn.net/kk55guang2/article/details/86179021 跟我的思想一樣,解釋的很清晰

 

45、把數組排成最小的數

題目描述:

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

class Solution {
public:
    string PrintMinNumber(vector<int> numbers) {
        
        if(numbers.size()==0)
            return {};
        string s;
        sort(numbers.begin(), numbers.end(), compare);
        
        for(int i=0;i<numbers.size();i++)
            s += to_string(numbers[i]);
        return s;
    }
    
    static bool compare(int a, int b)
    {
        string s1,s2;
        
        s1 = to_string(a) + to_string(b);
        s2 = to_string(b) + to_string(a);
        return s1<s2;
    }
};

注:兩個數組合起來比較。

 

46、把數字翻譯成字符串

題目描述:leetcode:91. 解碼方法

一條包含字母 A-Z 的消息通過以下方式進行了編碼:

'A' -> 1,'B' -> 2,...'Z' -> 26
給定一個只包含數字的非空字符串,請計算解碼方法的總數。

class Solution {
public:
    int numDecodings(string s) {
        if (s.empty() || (s.size() > 1 && s[0] == '0')) return 0;
        vector<int> dp(s.size() + 1, 0);
        dp[0] = 1;
        for (int i = 1; i < dp.size(); ++i) {
            dp[i] = (s[i - 1] == '0') ? 0 : dp[i - 1];
            if (i > 1 && (s[i - 2] == '1' || (s[i - 2] == '2' && s[i - 1] <= '6'))) {
                dp[i] += dp[i - 2];
            }
        }
        return dp.back();
    }
};

這是典型的動態規劃問題
dp[i]表示前i個字符的解碼方式。那麼考慮加進來的第i個字符,如果i個字符可以自己構成一個信息,也就第i個不等於0,那麼dp[i] = dp[i-1],如果i和i-1合在一起還能表示一個信息,那麼這時,dp[i] = dp[i-2]
所以考慮上面的情況,我們可以分析得出,當第i個字符不等於0的時候,最少dp[i] = dp[i-1],如果i和i-1還能構成一個信息,也就是在11-19到21-26之間的時候,自然dp[i] = dp[i-1]+ dp[i-2].

https://blog.csdn.net/dongyanwen6036/article/details/86559178 , 

https://www.cnblogs.com/ariel-dreamland/p/9159520.html

 

47、禮物的最大價值

題目描述:Leetcode64:最小路徑和

給定一個包含非負整數的 m x n 網格,請找出一條從左上角到右下角的路徑,使得路徑上的數字總和爲最小。

說明:每次只能向下或者向右移動一步。

class Solution {
public:
    int minPathSum(vector<vector<int>>& grid) {
        
        if(grid.size()==0 || grid[0].size()==0)
            return 0;
        
        for(int i=1;i<grid[0].size();i++)
            grid[0][i] = grid[0][i] + grid[0][i-1];
            
        for(int i=1;i<grid.size();i++)
            grid[i][0] = grid[i][0] + grid[i-1][0];
        
        for(int i=1;i<grid[0].size();i++)
            for(int j=1;j<grid.size();j++)  
                grid[j][i] = grid[j][i] + min(grid[j][i-1], grid[j-1][i]);
        
        return grid[grid.size()-1][grid[0].size()-1];
    }
};

注:動態規劃。

 

48、最長不含重複字符的子字符串

題目描述:Leetcode3:無重複字符的最長子串

給定一個字符串,請你找出其中不含有重複字符的 最長子串 的長度。

class Solution {
public:
    int lengthOfLongestSubstring(string s) {
        
        if(s.size()==0)
            return 0;
        
        int maximum = 0, temp = 0, left = 0;
        map<char,int> hash;
        
        for(int i=0;i<s.size();i++)
        {
            if(hash.find(s[i])==hash.end() || hash[s[i]]<left)
            {
                ++temp;
                hash[s[i]] = i;
            }
            else 
            {
                left = hash[s[i]] + 1;
                temp = i - left + 1;
                hash[s[i]] = i;                
            }
            
            maximum = max(maximum, temp);
        }
        return maximum;
    }
};

注:相當於運用雙指針法計算長度。

 

49、醜數

題目描述:

把只包含質因子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> cache(index,1);
        
        int Ugly_2 = 2, Ugly_3 = 3, Ugly_5 = 5;
        int Uglyptr_2 = 0, Uglyptr_3 = 0, Uglyptr_5 = 0;
        int ptr = 1;
        
        while(ptr<index)
        {
            int minimum = MIN(Ugly_2*cache[Uglyptr_2], Ugly_3*cache[Uglyptr_3], Ugly_5*cache[Uglyptr_5]);
            
            if(Ugly_2*cache[Uglyptr_2] == minimum)
            {
                cache[ptr] = Ugly_2*cache[Uglyptr_2++];
            }
            
            if(Ugly_3*cache[Uglyptr_3] == minimum)
            {
                cache[ptr] = Ugly_3*cache[Uglyptr_3++];
            }
            
            if(Ugly_5*cache[Uglyptr_5] == minimum)
            {
                cache[ptr] = Ugly_5*cache[Uglyptr_5++];
            }
            ptr++;
        }
        return cache[index-1];
    }
    
    int MIN(int a, int b, int c)
    {
        int temp = min(a, b);
        return min(temp, c);
    }
};

注:使用空間換時間的方法,我們不需要挨個判斷醜數,只需要找2/3/5他們三個的乘積從小到大排列就可以了,定義三個指針和一個數組記錄從小到大的乘積(也就是醜數)即可。

 

50、第一個只出現一次的字符

題目一描述:第一個只出現一次的字符

在一個字符串(0<=字符串長度<=10000,全部由字母組成)中找到第一個只出現一次的字符,並返回它的位置, 如果沒有則返回 -1(需要區分大小寫).

class Solution {
public:
    int FirstNotRepeatingChar(string str) {
        
        map<char, int> hash;
        
        for(int i=0;i<str.size();i++)
        {
            if(hash.find(str[i]) == hash.end())
                hash[str[i]] = 1;
            else hash[str[i]]++;
        }
        
        for(int i=0;i<str.size();i++)
            if(hash[str[i]]==1)
                return i;
        
        return -1;
    }
};

注:使用空間換時間,定義哈希表遍歷一次,然後再驗證一次即可。

題目二描述:字符流中第一個不重複的字符

請實現一個函數用來找出字符流中第一個只出現一次的字符。例如,當從字符流中只讀出前兩個字符"go"時,第一個只出現一次的字符是"g"。當從該字符流中讀出前六個字符“google"時,第一個只出現一次的字符是"l"。如果當前字符流沒有存在出現一次的字符,返回#字符。

class Solution
{
private:
    string str;
    map<char,int> hash;
public:
  //Insert one char from stringstream
    void Insert(char ch)
    {
        if(hash.find(ch)==hash.end())
            hash[ch] = 1;
        else hash[ch]++;
        
        str.push_back(ch);
    }
  //return the first appearence once char in current stringstream
    char FirstAppearingOnce()
    {
        for(char ch :str)
            if(hash[ch]==1)
                return ch;
        return '#';
    }

};

注:與題目一思想一致。

 

51、數組中的逆序對

題目描述:

在數組中的兩個數字,如果前面一個數字大於後面的數字,則這兩個數字組成一個逆序對。輸入一個數組,求出這個數組中的逆序對的總數P。並將P對1000000007取模的結果輸出。 即輸出P%1000000007

輸入描述:題目保證輸入的數組中沒有的相同的數字

數據範圍:對於%50的數據,size<=10^4;對於%75的數據,size<=10^5;對於%100的數據,size<=2*10^

class Solution {
public:
    int InversePairs(vector<int> data) {
        
        if(data.size()==0)
            return 0;
        
        long long num = 0;
        MergeSort(data, 0, data.size()-1, num);
        
        return num%1000000007;
    }
    
    void MergeSort(vector<int>& data, int left, int right, long long &num)
    {
        if(left<right)
        {
            int mid = left + (right - left)/2;
            
            MergeSort(data, left, mid, num);
            MergeSort(data, mid+1, right, num);
            Merge(data, left, mid, right, num);
        }
    }
    
    void Merge(vector<int> &data, int left, int mid, int right, long long &num)
    {
        if(right>left)
        {
            vector<int> cache(right-left+1, 0);
            int p = mid, q = right, r = right-left;
            
            while(p>=left || q>=mid+1)
            {
                int a = INT_MIN, b = INT_MIN;
                
                if(p>=left)
                    a = data[p];
                if(q>=mid+1)
                    b = data[q];
                
                if(b>a)
                {
                    cache[r] = b;
                    --r;
                    --q;
                }
                else
                {
                    cache[r] = a;
                    --r;
                    --p;
                    if(b!=INT_MIN)
                        num += q-mid;
                }
            }
            
            for(int i=0;i<right-left+1;i++)
                data[i+left] = cache[i];
        }
    }
};

注:利用歸併排序的思想找逆序對即可,需要注意num得設置爲longlong類型。

 

52、兩個鏈表的第一個公共結點

題目描述:

輸入兩個鏈表,找出它們的第一個公共結點。

/*
struct ListNode {
	int val;
	struct ListNode *next;
	ListNode(int x) :
			val(x), next(NULL) {
	}
};*/
class Solution {
public:
    ListNode* FindFirstCommonNode( ListNode* pHead1, ListNode* pHead2) {
        
        stack<ListNode*> s1,s2;
        
        while(pHead1)
        {
            s1.push(pHead1);
            pHead1 = pHead1->next;
        }
        
        while(pHead2)
        {
            s2.push(pHead2);
            pHead2 = pHead2->next;
        }
        
        ListNode* p = nullptr;
        
        while(!s1.empty() && !s2.empty())
        {
            if(s1.top() == s2.top())
            {
                p = s1.top();
                s1.pop();
                s2.pop();
            }
            else break;
        }
        return p;
    }
};

注:將兩個鏈表全部壓入棧,從尾結點開始對比,如果不同則輸出後一個節點。還有一種是先計算鏈表的長度,然後使用快慢指針在鏈表上,快指針比慢指針先走了兩鏈表長度之差。

 

53、數字在排序數組中出現的次數

題目描述:

統計一個數字在排序數組中出現的次數。

class Solution {
public:
    int GetNumberOfK(vector<int> data ,int k) {
        
        if(data.size()==0)
            return 0;
        
        int left = 0, right = data.size();
        
        while(left<right)
        {
            int mid = left + (right - left)/2;
            
            if(data[mid]<k)
                left = mid + 1;
            else right = mid;
        }
        if(left>=data.size() || data[left]!=k)
            return 0;
        
        int first = left;
        left = 0, right = data.size();
        
        while(left<right)
        {
            int mid = left + (right - left)/2;
            
            if(data[mid]>k)
                right = mid;
            else left = mid + 1;
        }
        int last = left;
        return last - first;
    }
};

注:應用二分查找法,找到第一個出現和最後一個出現的索引,然後相減即可。

 

54、二叉搜索樹的第k個結點

題目描述:

給定一棵二叉搜索樹,請找出其中的第k小的結點。例如, (5,3,7,2,4,6,8)    中,按結點數值大小順序第三小結點的值爲4。

/*
struct TreeNode {
    int val;
    struct TreeNode *left;
    struct TreeNode *right;
    TreeNode(int x) :
            val(x), left(NULL), right(NULL) {
    }
};
*/
class Solution {
public:
    TreeNode* KthNode(TreeNode* pRoot, int k)
    {
        if(k<=0 || !pRoot)
            return nullptr;
        
        vector<int> inorder;
        Inorder(inorder, pRoot);
        
        if(k>inorder.size())
            return nullptr;
        
        int target = inorder[k-1];
        
        return FindTargetNode(pRoot, target);
        
    }
    
    TreeNode* FindTargetNode(TreeNode* pRoot, int target)
    {
        if(pRoot->val == target)
            return pRoot;
        else if(pRoot->val>target)
            return FindTargetNode(pRoot->left, target);
        else return FindTargetNode(pRoot->right, target);
    }
    
    void Inorder(vector<int> &inorder, TreeNode* pRoot)
    {
        if(!pRoot)
            return ;
        
        Inorder(inorder, pRoot->left);
        inorder.push_back(pRoot->val);
        Inorder(inorder, pRoot->right);
    }
};

注:中序遍歷即可。

 

55、二叉樹的深度

題目一描述:二叉樹的深度

輸入一棵二叉樹,求該樹的深度。從根結點到葉結點依次經過的結點(含根、葉結點)形成樹的一條路徑,最長路徑的長度爲樹的深度。

/*
struct TreeNode {
	int val;
	struct TreeNode *left;
	struct TreeNode *right;
	TreeNode(int x) :
			val(x), left(NULL), right(NULL) {
	}
};*/
class Solution {
public:
    int TreeDepth(TreeNode* pRoot)
    {
        if(!pRoot)
            return 0;
        
        return 1 + max(TreeDepth(pRoot->left), TreeDepth(pRoot->right));
    }
};

注:遞歸求解即可。

題目二描述:平衡二叉樹

輸入一棵二叉樹,判斷該二叉樹是否是平衡二叉樹。

class Solution {
public:
    bool IsBalanced_Solution(TreeNode* pRoot) {
        
        int depth = 0;
        
        return IsBalanced(pRoot,depth); 
    }
    
    bool IsBalanced(TreeNode* pRoot, int &depth) 
    {
        if(!pRoot)
            return true;
        depth++;
        int leftdepth = depth, rightdepth = depth;
        
        bool left = IsBalanced(pRoot->left, leftdepth); 
        bool right = IsBalanced(pRoot->right, rightdepth); 
        
        depth = max(leftdepth, rightdepth);
        return left && right && (abs(leftdepth-rightdepth)<=1);
    }
};

注:本題遞歸求解會導致大量的重複子問題,解決方法是從下至上的遍歷。

 

56、數組中只出現一次的數字

題目描述:

一個整型數組裏除了兩個數字之外,其他的數字都出現了兩次。請寫程序找出這兩個只出現一次的數字。

class Solution {
public:
    void FindNumsAppearOnce(vector<int> data,int* num1,int *num2) {
        
        if(data.size()==0)
            return ;
        
        int cache = data[0];
        for(int i=1;i<data.size();i++)
            cache^=data[i];
        
        int bit=0;
        while((1 & cache) != 1 && bit<8*sizeof(int))
        {
            cache = cache>>1;
            bit++;
        }
        
        *num1 = 0, *num2 = 0;
        for(int i=0;i<data.size();i++)
        {
            if(IsOne(data[i], bit))
                *num1^=data[i];
            else *num2^=data[i];
        }
    }
    
    bool IsOne(int num, int bit)
    {
        num = num>>bit;
        return (num & 1);
    }
};

注:如果面試時沒思路直接就用哈希表。

 

 

57、和爲S的數字

題目一描述:和爲S的兩個數字

輸入一個遞增排序的數組和一個數字S,在數組中查找兩個數,使得他們的和正好是S,如果有多對數字的和等於S,輸出兩個數的乘積最小的。

class Solution {
public:
    vector<int> FindNumbersWithSum(vector<int> array,int sum) {
        
        int left = 0, right = array.size()-1;
        int multyply = INT_MAX,num1,num2;
        
        while(left<right)
        {
            if(array[left]+array[right]<sum)
                ++left;
            else if(array[left]+array[right]>sum)
                --right;
            else
            {
                if(array[left]*array[right]<multyply)
                {
                    num1 = array[left];
                    num2 = array[right];
                    multyply = num1 * num2;
                }
                ++left;
                --right;
            }
        }
        if(multyply == INT_MAX)
            return {};
        return {num1, num2};
    }
};

注:本題的數組是連續的,所以是典型的雙指針算法應用場景,分別設定左右指針在數組的首尾,兩數之和大於target,則right指針自減,反之則left指針自加,由於本題還需要求最小的成績,所以還需要引入哨兵。本題測試用例不考慮乘積越界。

題目二描述:和爲S的連續正數序列

小明很喜歡數學,有一天他在做數學作業時,要求計算出9~16的和,他馬上就寫出了正確答案是100。但是他並不滿足於此,他在想究竟有多少種連續的正數序列的和爲100(至少包括兩個數)。沒多久,他就得到另一組連續正數和爲100的序列:18,19,20,21,22。現在把問題交給你,你能不能也很快的找出所有和爲S的連續正數序列? Good Luck!

class Solution {
public:
    vector<vector<int> > FindContinuousSequence(int sum) {
        
        vector<vector<int> > output;
        vector<int> cache;
        int left = 1, cumsum = 0;
        
        for(int i=1;i<sum;i++)
        {
            cumsum +=i;
            
            while(cumsum>sum)
            {
                cumsum -= left;
                left ++;
            }
            
            if(cumsum == sum)
            {
                for(int j=left;j<=i;j++)
                    cache.push_back(j);
                output.push_back(cache);
                cache.clear();
            }
        }
        return output;
    }
};

注:本題的思想與題目一完全一致。

 

58、翻轉單詞順序列

題目一描述:翻轉單詞順序列

牛客最近來了一個新員工Fish,每天早晨總是會拿着一本英文雜誌,寫些句子在本子上。同事Cat對Fish寫的內容頗感興趣,有一天他向Fish借來翻看,但卻讀不懂它的意思。例如,“student. a am I”。後來才意識到,這傢伙原來把句子單詞的順序翻轉了,正確的句子應該是“I am a student.”。Cat對一一的翻轉這些單詞順序可不在行,你能幫助他麼?

class Solution {
public:
    string ReverseSentence(string str) {
        
        if(str.size()==0)
            return str;
        
        int start = 0;
        
        for(int i=0;i<str.size();i++)
        {
            if(str[i]==' ')
            {
                if(str[start]==' ')
                {
                    start = i+1;
                    continue;
                }
                reverse(str.begin()+start, str.begin()+i);
                start = i+1;
            }
        }
        if(start<str.size())
            reverse(str.begin()+start, str.end());
        
        reverse(str.begin(), str.end());
        return str;
    }
};

注:先翻轉每一個單詞,然後再翻轉整個字符串。

題目二描述:左旋轉字符串

彙編語言中有一種移位指令叫做循環左移(ROL),現在有個簡單的任務,就是用字符串模擬這個指令的運算結果。對於一個給定的字符序列S,請你把其循環左移K位後的序列輸出。例如,字符序列S=”abcXYZdef”,要求輸出循環左移3位後的結果,即“XYZdefabc”。是不是很簡單?OK,搞定它!

class Solution {
public:
    string LeftRotateString(string str, int n) {
        
        if(str.size()==0)
            return str;
        int size = str.size();
        n %= size;
        if(n==0)
            return str;
        
        reverse(str.begin(), str.begin()+n);
        reverse(str.begin()+n, str.end());
        reverse(str.begin(), str.end());
        return str;
    }
};

注:本題的思想與題目一完全一致,需要注意當n大於等於str.size()時,要求餘。

 

59、隊列的最大值

題目一描述:滑動窗口的最大值

給定一個數組和滑動窗口的大小,找出所有滑動窗口裏數值的最大值。例如,如果輸入數組{2,3,4,2,6,2,5,1}及滑動窗口的大小3,那麼一共存在6個滑動窗口,他們的最大值分別爲{4,4,6,6,6,5}; 針對數組{2,3,4,2,6,2,5,1}的滑動窗口有以下6個: {[2,3,4],2,6,2,5,1}, {2,[3,4,2],6,2,5,1}, {2,3,[4,2,6],2,5,1}, {2,3,4,[2,6,2],5,1}, {2,3,4,2,[6,2,5],1}, {2,3,4,2,6,[2,5,1]}。

class Solution {
public:
    vector<int> maxInWindows(const vector<int>& num, unsigned int size)
    {
        if(num.size()==0 || size > num.size() || size==0)
            return {};
        
        deque<int> q;
        vector<int> output;
        
        for(int i=0;i<size;i++)
        {
            while(!q.empty() && num[q.back()]<=num[i])
                q.pop_back();
            q.push_back(i);
        }
        
        for(int i=size;i<num.size();i++)
        {
            output.push_back(num[q.front()]);
                
            while(!q.empty() && num[q.back()]<=num[i])
                q.pop_back();
            if(!q.empty() && q.front()<=i-size)
                q.pop_front();
                
            q.push_back(i);
        }
        output.push_back(num[q.front()]);
        return output;
    }
};

注:維護一個雙端隊列。

 

60、n個骰子的點數

 

注:略。

 

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。

class Solution {
public:
    bool IsContinuous( vector<int> numbers ) {
        
        if(numbers.size()==0)
            return false;
        
        sort(numbers.begin(), numbers.end());
        
        int left = 0;
        for(int i=numbers.size()-1;i>=left;i--)
        {
            if(i-1>=0 && numbers[i-1]!=0 && (numbers[i]-numbers[i-1]>1 || numbers[i]-numbers[i-1]==0))
            {
                int gap = numbers[i]-numbers[i-1]-1;
                while(numbers[left]==0 && gap>0)
                {
                    left++;
                    gap--;
                }
                if(gap!=0)
                    return false;
            }
        }
        return true;
    }
};

注:先將數組進行排序,然後從後往前遍歷,看看0的數量是不是能填補空缺。

 

62、孩子們的遊戲(圓圈中最後剩下的數)

題目描述:

每年六一兒童節,牛客都會準備一些小禮物去看望孤兒院的小朋友,今年亦是如此。HF作爲牛客的資深元老,自然也準備了一些小遊戲。其中,有個遊戲是這樣的:首先,讓小朋友們圍成一個大圈。然後,他隨機指定一個數m,讓編號爲0的小朋友開始報數。每次喊到m-1的那個小朋友要出列唱首歌,然後可以在禮品箱中任意的挑選禮物,並且不再回到圈中,從他的下一個小朋友開始,繼續0...m-1報數....這樣下去....直到剩下最後一個小朋友,可以不用表演,並且拿到牛客名貴的“名偵探柯南”典藏版(名額有限哦!!^_^)。請你試着想下,哪個小朋友會得到這份禮品呢?(注:小朋友的編號是從0到n-1)

class Solution {
public:
    int LastRemaining_Solution(int n, int m)
    {
        
        if(n<1 || m<1)
            return -1;
        
        list<int> q;
        
        for(int i=0;i<n;i++)
            q.push_back(i);
        
        auto iter = q.begin();
        while(q.size()>1)
        {
            for(int i=1;i<m;i++)
            {
                ++iter;
                if(iter == q.end())
                    iter = q.begin();
            }
            
            auto next = ++iter;
            if(next == q.end())
                next = q.begin();
            
            --iter;
            q.erase(iter);
            iter = next;
        }
        return *iter;
    }
};

注:使用list模擬環形鏈表。

 

 

63、股票的最大利潤

題目描述:(Leetcode121. 買賣股票的最佳時機

給定一個數組,它的第 i 個元素是一支給定股票第 i 天的價格。如果你最多隻允許完成一筆交易(即買入和賣出一支股票),設計一個算法來計算你所能獲取的最大利潤。注意你不能在買入股票前賣出股票。

class Solution {
public:
    int maxProfit(vector<int>& prices) {
        
        if(prices.size()<2)
            return 0;
        
        int temp = 0, maximum = 0;
            
        for(int i=1;i<prices.size();i++)
        {
            temp += (prices[i]-prices[i-1]);
            maximum = max(temp, maximum);
            if(temp<=0)
                temp = 0;
        }
        return maximum;
    }
};

注:與連續子數組最大和的思想完全一致,不過本題內使用的是後一項減去前一項的差值。

 

64、求1+2+3+...+n

題目描述:

求1+2+3+...+n,要求不能使用乘除法、for、while、if、else、switch、case等關鍵字及條件判斷語句(A?B:C)。

class Solution {
public:
    int Sum_Solution(int n) {
        
        int sum = n;
        bool flag = (n!=0) && (sum += Sum_Solution(n-1));
        return sum;
    }
};

注:利用短路特性來做遞歸條件。

 

65、不用加減乘除做加法

題目描述:

寫一個函數,求兩個整數之和,要求在函數體內不得使用+、-、*、/四則運算符號。

class Solution {
public:
    int Add(int num1, int num2)
    {
        int sum, carry;
        
        do
        {
            sum = num1^num2;
            carry = (num1 & num2)<<1;
            
            num1 = carry;
            num2 = sum;
        }
        while(num1!=0);
        
        return num2;
    }
};

注:採用三步走的方式,首先第一步用異或相加但不進位,第二步求與左移一位算進位,第三步相加,循環即可。

 

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 Solution {
public:
    vector<int> multiply(const vector<int>& A) {
        
        if(A.size()==0)
            return {};
        
        vector<int> B(A.size(), 1);
        
        for(int i=1;i<A.size();i++)
            B[i] = B[i-1] * A[i-1];
        
        int temp = 1;
        for(int i=A.size()-2;i>=0;i--)
        {
            temp *= A[i+1];
             B[i] *= temp;
        }
        return B;
    }
};

注:分成兩段來考慮,每一段都是增量乘積。

 

67、把字符串轉換成整數

題目描述:

將一個字符串轉換成一個整數(實現Integer.valueOf(string)的功能,但是string不符合數字要求時返回0),要求不能使用字符串轉換整數的庫函數。 數值爲0或者字符串不是一個合法的數值則返回0。

輸入描述:

輸入一個字符串,包括數字字母符號,可以爲空

輸出描述:

如果是合法的數值表達則返回該數字,否則返回0
class Solution {
public:
    int StrToInt(string str) {
        
        bool flag = true;
        int sign = 1;
        int num = 0;
        
        for(int i=0;i<str.size();i++)
        {
            if(flag && str[i]==' ')
                continue;
            else if(flag && (str[i]=='-' || str[i]=='+'))
            {
                if(str[i]=='-')
                    sign = -1;
                flag = false;
            }
            else if(str[i]>='0' && str[i]<='9')
            {
                num = num * 10 + str[i] - '0';
                flag = false;
            }
            else return 0;
        }
        if(sign == -1)
            return -1 * num;
        return num;
    }
};

注:注意各種邊界條件即可,本題中沒有考慮num越界以及正負號非法的測試用例。

 

68、樹中兩個節點的最低公共祖先

題目描述:

給定一個二叉樹, 找到該樹中兩個指定節點的最近公共祖先。百度百科中最近公共祖先的定義爲:“對於有根樹 T 的兩個結點 p、q,最近公共祖先表示爲一個結點 x,滿足 x 是 p、q 的祖先且 x 的深度儘可能大(一個節點也可以是它自己的祖先)。”

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        if(root == NULL) {
            return NULL;
        }
        if(root == p || root == q) {
            return root;
        }
        TreeNode* left = lowestCommonAncestor(root->left, p, q);
        TreeNode* right = lowestCommonAncestor(root->right, p, q);
        if(left != NULL && right != NULL) {
            return root;
        } else if(left != NULL) {
            return left;
        } else {
            return right;
        }
        
    }
};

注:

如果root爲null或者p、q中有一個就是root,直接返回root即可。

在左子樹中尋找p、q的最近公共祖先,記錄所得結果爲left。

在右子樹中尋找p、q的最近公共祖先,記錄所得結果爲right。

如果left爲null,直接返回right即可。

如果right爲null,直接返回left即可。

如果left和right均不爲null,我們應該直接返回root。

時間複雜度是O(n),其中n爲二叉樹中的節點數。空間複雜度是O(h),其中h爲二叉樹的高度。

https://blog.csdn.net/qq_41231926/article/details/87092480

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章