1. 程式人生 > >劍指offer解題思路錦集11-20題

劍指offer解題思路錦集11-20題

難點 nbsp 因此 div sub AR 類型 lis 實現一個函數

又來更新劍指offer上的題目思路啦。

11、【二進制中1的個數】

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

eg:NumberOf1(1)=1
NumberOf1(2)=0
NumberOf1(3)=2
NumberOf1(4)=1
NumberOf1(5)=2
NumberOf1(6)=2
NumberOf1(7)=3

思路:每次都將數字n的最後一位1反轉成0,不斷反轉到這個數字變成0,然後我們統計反轉了多少次,這樣不就可以成功得到這個數字有多少位了嗎?

難點:如何反轉一個數字的最後一位1,解決方案如下:

n = n & (n - 1);

代碼:

     int  NumberOf1(int n) {
         int number = 0;
         while (n != 0)
         {
         	n = n & (n - 1);
         	++number; 
         }
         return number;
     }

思路2:可不可以嘗試不去改變這個數字呢?答案也可以的。假設是int類型,如果我循環32次,判斷每個位是否為1,這樣不也可以得到這個數字有多少位為1嗎?誠然,這樣也是存在著缺點的。

缺點:比思路1的時間復雜度略高,不過也是常數級別的,因為數字的位數是有限制的,兩者可能系數上存在著差別吧!~~~還有一個缺點,就是這樣的代碼移植性較差~~

存在坑:

(temp & n) != 0

這裏的括號不能去掉,因為&的優先級比 != 低。。。。

代碼:

     int  NumberOf1(int n) {
         int number = 0;
         int temp = 1;
         for (int i = 0; i < 32; ++i)
         {
             if ((temp & n) != 0)
             {
                 ++number;
             }
             temp = temp<<1;
         }
         return number;
     }

12、【數值的整數次方】

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

例子:2^10

思路1:2*2*2*2*2*2*2*2*2*2,循環N次即可。時間復雜度O(n)沒啥好說的,跳過。

思路2: 分治想法,假設是求a^n

  如果n為偶數,那麽轉化成求x=a^(n/2),然後返回x*x即可。

  如果n為奇數,那麽轉化成求x=a^(n-1/2),然後返回x*x*a即可。

  時間復雜度是O(lgn)。。。怎麽證明?

應用算法導論中的主方法即可。

技術分享圖片

這裏,有T(n) = T(n/2) + 1,即 b = 2, a = 1,f(n)=1。因此滿足條件2,因此時間復雜度為O(nlogb(a))=O(nlog2(1)*lgn)=O(1*lgn)=O(lgn)。

哈哈,是不是瞬間感覺寫難了。。。。

13、【調整數組順序使奇數位於偶數前面】

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

思路:兩個數組,一個按順序保存奇數,另一個按順序保存偶數。。。時間復雜度O(N),空間復雜度O(N)。

思路2:創建一個數組,循環2次,第一次只掃描奇數,丟進數組中。第二次只掃描偶數,丟進數組。。十分簡單。。。

思路3:類似冒泡算法,前偶後奇數就交換。

    void reOrderArray(vector<int> &arr) 
    {//雙指針
        int n = arr.size();
        for(int i = 0; i < n; ++i)
        {
            for(int j = 0; j < n-i-1; ++j)
            {
                if(arr[j]%2 == 0 && arr[j+1]%2 == 1) swap(arr[j],arr[j+1]);
            }
        }
    }

思路4:兩個指針咯,一個指針1從頭到尾,另外一個指針2從尾到頭,如果指針1發現偶數,那麽停下來,如果指針2發現奇數,那麽也停下來。再交換兩個指針指向的數就Ok了。

   問題:這樣是真的可以嗎?答案是不可以的哦~哈哈,因為這樣違反了題目要求——要求奇數和偶數順序不能改變。。。嗯,是的,如果是讓順序可以隨意的話,那麽這就是很nice的解。

14、【鏈表中倒數第k個結點】

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

思路1:遍歷得到鏈表長度n,然後再遍歷一次,給返回第n-k+1個元素。這裏遍歷了兩次,時間復雜度為0(n),空間復雜度為O(1)。

思路2:思路1是否存在優化的地方呢?是存在的,我們可以考慮考慮能不能不遍歷兩次,這時候我們想能不能用空間換時間。

    好比,將鏈表轉化成數組保存下來,那麽我們第二次遍歷鏈表操作可以換成獲取數組中的n-k+1個元素操作了,只需要遍歷一次鏈表就可以了。但是這種方法的空間復雜度為O(N)。

思路3:兩個指針,也就是傳說中的快慢指針。一個指針先走k步,然後慢指針才開始跑。快指針到頭了,那麽落後了k步的慢指針不就是倒數第k個元素了嗎?

代碼:

    ListNode* FindKthToTail(ListNode* pListHead, unsigned int k) 
    {
        if(NULL == pListHead) return NULL;
        ListNode* pfront = pListHead;
        ListNode* pend = pListHead;
        for (int i = 0; i < k; i++)
        {
            if(pfront != NULL)
            {
                pfront = pfront->next;
            }
            else
            {
                return NULL;//超出了
            }
        }
        for(;pfront != NULL;pfront = pfront->next)
        {
            pend = pend->next;
        }
        return pend;
    }

15、【反轉鏈表】

輸入一個鏈表,反轉鏈表後,輸出鏈表的所有元素。

A->B->C->D->E
    
A->B->C->D<-E (D->NULL)
 
A->B->C<-D<-E (C->NULL)
 
A->B<-C<-D<-E (B->NULL)
 
A<-B<-C<-D<-E (A->NULL)

代碼

    ListNode* ReverseList(ListNode* pHead)
    {
        if(pHead == NULL||pHead->next == NULL) return pHead;//得到鏈表尾部
        ListNode* tail = ReverseList(pHead->next);
        pHead->next->next = pHead;
        pHead->next = NULL;
        return tail;
    }

16、【合並兩個排序的鏈表】

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

額,這題啥也不用說了吧,,,不就是歸並排序的合並過程麽。。。。直接貼代碼吧。。。

    ListNode* Merge(ListNode* pHead1, ListNode* pHead2)
    {
        ListNode* res = NULL;
        ListNode* cur = NULL;
        ListNode* add = NULL;
        if(pHead1 == NULL) return pHead2;
        if(pHead2 == NULL) return pHead1;
        while(pHead1 != NULL &&pHead2 != NULL)//兩個叠代器
        {
            add = pHead1->val >= pHead2->val ?pHead2:pHead1;
            if(res == NULL)  res = cur = add;////////////第一次才執行 在於選擇一條鏈出來
            else
            {
                cur->next = add;
                cur = cur->next;
            }
            if(add == pHead1) pHead1=pHead1->next;
            else pHead2 = pHead2->next;
        }
        if(pHead1 != NULL) cur->next = pHead1;
        if(pHead2 != NULL) cur->next = pHead2;
        return res;
    }

17、【樹的子結構】

題目:輸入兩棵二叉樹A,B,判斷B是不是A的子結構。(ps:我們約定空樹不是任意一個樹的子結構)

其實這裏主要是靠對問題的拆分能力。

  子函數CompTree:判斷兩棵樹是否相等。

  判斷是否為子樹:假設當前節點為根,那麽是否為子結構?

代碼:

class Solution {
private:
    bool CompTree(TreeNode* pRoot1, TreeNode* pRoot2)//判斷2是否為1的子樹。。。。
    {
        if(pRoot2 == NULL) return true;
        if(pRoot1 == NULL) return false;
        
        if(pRoot1->val == pRoot2->val) 
            return CompTree(pRoot1->left,pRoot2->left)&&CompTree(pRoot1->right,pRoot2->right);
        return false;
    }
public:
    bool HasSubtree(TreeNode* pRoot1, TreeNode* pRoot2)
    {
        if(pRoot1 == NULL ||pRoot2 == NULL ) return false;
		return CompTree(pRoot1,pRoot2) || HasSubtree(pRoot1->left,pRoot2) || HasSubtree(pRoot1->right, pRoot2);
    }
};

18、

劍指offer解題思路錦集11-20題