1. 程式人生 > >K:劍指offer-56 題解 誰說數位電路的知識不能用到演算法中?從次數統計到數位電路公式推導,一文包你全懂

K:劍指offer-56 題解 誰說數位電路的知識不能用到演算法中?從次數統計到數位電路公式推導,一文包你全懂

前言:

本題解整理了一位大佬在leetcode中的程式碼的方法,該博文致力於讓所有人都能夠能夠看懂該方法。為此,本題解將從統計數字出現次數的解題方式開始講起,再推匯出逐位統計的解題方式,期望以循序漸進的方式得出最終程式碼的思想。

相關知識關鍵字:

二進位制、位運算、真值表、邏輯表示式、狀態機

題目:

劍指offer 56 II. 陣列中數字出現的次數 II

在一個數組 nums 中除一個數字只出現一次之外,其他數字都出現了三次。請找出那個只出現一次的數字。

示例 1:
輸入:nums = [3,4,3,3]
輸出:4

示例 2:
輸入:nums = [9,1,7,9,7,9,7]
輸出:1

題解:

對於陣列nums,其只有一個數字出現了一次,其餘數字均出現了三次,一種直觀的想法是直接採用一個map統計各個字元出現的次數,最後再遍歷map中的各個鍵值對,直到找到只出現了一次的數字。其程式碼如下

    public int singleNumber(int[] nums) {
        //統計各個數字出現的次數,鍵為數字,值為出現的次數
        Map<Integer,Integer> map =new HashMap<Integer,Integer>();
        for(int i:nums){
            if(!map.containsKey(i)){
                map.put(i,1);
                continue;
            }
            map.put(i,map.get(i)+1);
        }
        //遍歷map中的鍵值對,檢視值出現次數為1的鍵,即為答案
        int result = 0;
        for(Map.Entry<Integer,Integer> entry:map.entrySet()){
            if(entry.getValue()==1){
                result = entry.getKey();
                break;
            }
        }
        return result;
    }

對於該解題方法,其空間複雜度為O(n),時間複雜度為O(n),這顯然不會是該題的最優解。

在得出逐位運算的解題方式之前,我們需要研究下該陣列中的數字用二進位制的方式進行表示的特點。

以題幹給出的示例1為例,nums=[3,4,3,3],將陣列中各個數字採用二進位制的方式寫出,
3 = (0011)2
4 = (0100)2
3 = (0011)2
3 = (0011)2

通過對陣列中各個數的二進位制表示形式逐位進行觀察,我們可以發現,當陣列中只出現一次的那個數字(用k表示)在二進位制的對應位為0時,該對應位為1在陣列各個數字中出現的總次數應當為3^n ,當k的對應位為1時,該對應位為1在陣列各個數字中出現的總次數應當為 3^n + 1,為此,我們可以統計數字中的各個位中1出現的次數,當為3^n 次時,只出現一次的數字的對應位應當為0,當為3^n + 1次時,只出現一次的數字的對應位應當為1。由此,我們可以得到如下程式碼:

    public int singleNumber(int[] nums) {
        if(nums==null||nums.length==0) return 0;
        int result = 0;
        for(int i = 0;i<32;i++){
            //統計該位1的出現次數情況
            int count = 0;
            int index = 1<<i;
            for(int j:nums){
                //該位與操作後的結果不為0,則表示該位為1的情況出現了
                if((index&j)!=0){
                    count++;
                }
            }
            //該位上出現1的次數mod3後為1,表示出現一次的數字該位為1
            if(count%3==1){
                result|=index;
            }
        }
        return result;
    }

對於該解題方法,其時間複雜度為O(n),空間複雜度為O(1)。在某種程度上,這是最優解了。但是,該題解仍有改進的空間(其時間複雜度的常係數為32)。

有了對陣列中數字的各二進位制位進行逐一統計分析出現次數的相關基礎後,我們便可以推匯出那個擊敗100%的答案的解法了。回顧上面的解題方法的分析部分,其需要我們對數字的二進位制位逐位進行統計,對於int資料型別,我們需要遍歷32次陣列(int佔4位元組),以便統計出各個二進位制位出現的次數。那我們有沒有辦法只遍歷一次陣列便得出答案呢?當然有,我們可以一次分析32bit的int的各個位在陣列的各個數字中出現的次數。在分析上面的程式碼我們可以發現,實際上,我們只需要記錄對應位出現的次數為0、1、2次的情況,當對應位出現次數為3的時候,我們便可以將該位出現的次數置為0,重新開始進行計數。由於int型中的各個二進位制位出現的次數為3進位制的,為此我們需要兩個位來記錄各個位出現的次數,由此我們需要引入兩個變數a,b來統計對應位出現的次數。由ab兩個變數組合起來來記錄各個二進位制位出現為1的情況。變數a表示高位的情況,變數b表示低位的情況,而在遍歷陣列運算完成之後,遍歷b的值便是答案。

變數ab組合的各個二進位制位組合的形式有如下三種,考慮進新引入的變數c的各二進位制位的情況,我們可以得到如下真值表:

由以上真值表,我們便可得出變數a,b的邏輯表示式,其表示如下
a = a’(!b’)(!c)+(!a’)b’c
b = (!a’)b’(!c)+(!a’)(!b’)c = (!a’)[b’(!c)+(!b’)c] = (!a’)[b’^c]

由此,我們可以得到如下程式碼

    public int singleNumber(int[] nums) {
        //a對應位為1表示出現2次的記錄,b對應位表示出現1次或0次的記錄,ab共同組成該位出現的次數
        int a = 0,b =0;
        for(int i:nums){
            int temp = a;
            a = (~a&b&i)|(a&~b&~i);
            b = ~temp&(b^i);
        }
        return b;
    }

實際上,我們還能對a的邏輯表示式進行簡化,先得到b的邏輯表示式,之後用b代替b’作為輸入,由此可以簡化a為
a = (!a’)(!b)c+a’(!b)(!c) = (!b)[(!a’)c+a’(!c)] = (!b)[a’^c]

由此,我們可以得到如下程式碼

    public int singleNumber(int[] nums) {
        //a為對應位的1出現2次的記錄,b為對應位出現1次的記錄,ab共同組成該位出現的次數
        int a = 0,b =0;
        for(int i:nums){
            b = ~a&(b^i);
            a = ~b&(a^i);
        }
        return b;
    }

至此,我們得到了最終的程式碼。


這個是本人的公眾號,致力於寫出絕大部分人都能讀懂的技術文章。歡迎相互交流,我們博採眾長,共同進步。

相關推薦

Koffer-56 題解 數位電路知識不能演算法次數統計數位電路公式推導

前言: 本題解整理了一位大佬在leetcode中的程式碼的方法,該博文致力於讓所有人都能夠能夠看懂該方法。為此,本題解將從統計數字出現次數的解題方式開始講起,再推匯出逐位統計的解題方式,期望以循序漸進的方式得出最終程式碼的思想。 相關知識關鍵字: 二進位制、位運算、真值表、邏輯表示式、狀態機 題目: 劍指of

Koffer-56 題解 數位電路知識不能演算法次數統計到邏輯表示式的推導

前言: 本題解整理了一位大佬在leetcode中的程式碼的方法,該博文致力於讓所有人都能夠能夠看懂該方法。為此,本題解將從統計數字出現次數的解題方式開始講起,再推匯出逐位統計的解題方式,期望以循序漸進的方式得出最終程式碼的思想。 相關知識關鍵字: 二進位制、位運算、真值表、邏輯表示式、狀態機 題目: 劍指of

offer C++題解(牛客網)二維陣列的查詢

【二維陣列中的查詢】:在一個二維陣列中(每個一維陣列的長度相同),每一行都按照從左到右遞增的順序排序,每一列都按照從上到下遞增的順序排序。請完成一個函式,輸入這樣的一個二維陣列和一個整數,判斷陣列中是否含有該整數。 class Solution { public: bool Find

牛客網Offer習題集題解0

覆蓋 file print mta 題解 -m urn 打表 劍指offer https://www.nowcoder.com/ta/coding-interviews 牛客個人界面歡迎互fo 0x00 二維數組中的查找 沒啥難得,直接上二分就好了。註意二分別寫挫了。 時間

牛客網Offer C++題解

【二維陣列中的查詢】:在一個二維陣列中(每個一維陣列的長度相同),每一行都按照從左到右遞增的順序排序,每一列都按照從上到下遞增的順序排序。請完成一個函式,輸入這樣的一個二維陣列和一個整數,判斷陣列中是否含有該整數。 class Solution { public: bool Find

我編寫的 LeetCode 演算法題解和《 Offer題解

《LeetCode 題解》地址:https://liweiwei1419.github.io/leetcode-solution 2018 年 11 月 13 日總結 題號 要求 技巧 關鍵字 189 陣列向右旋轉

【Java】 offer(56-2) 陣列唯一隻出現次的數字 《Offer》Java實現合集 56-1) 陣列只出現次的兩個數字 《Offer》Java實現合集

  本文參考自《劍指offer》一書,程式碼採用Java語言。 更多:《劍指Offer》Java實現合集   題目   在一個數組中除了一個數字只出現一次之外,其他數字都出現了三次。請找出那個只出現一次的數字。 思路   這道題中數字出現了三次,無法像56-1) 陣列

offer 56. 刪除連結串列重複的結點

題目描述 在一個排序的連結串列中,存在重複的結點,請刪除該連結串列中重複的結點,重複的結點不保留,返回連結串列頭指標。 例如,連結串列1->2->3->3->4->4->5 處理後為 1->2->5 思路: 是有兩個迴圈判斷的控制,

Offer-56 陣列只出現次的兩個數字

題目: 一個整型數組裡除了兩個數字之外,其他的數字都出現了兩次。請寫程式找出這兩個只出現一次的數字。你可以假設這兩個數字一定存在。 樣例 輸入:[1,2,3,3,4,4] 輸出:[1,2] 解答: class Solution(object): def findNumsAppe

Python 尾到頭列印連結串列 【牛客網offer

# class ListNode: # def __init__(self, x): # self.val = x # self.next = None cl

【LeetCode & offer刷題】動態規劃與貪婪法題3Offer-46把數字翻譯成字串

【LeetCode & 劍指offer 刷題筆記】目錄(持續更新中...) 劍指Offer-46:把數字翻譯成字串 題目: 給定一個數字,我們按照如下規則把它翻譯為字串:0翻譯成“a”,1翻譯成“b”,……,11翻譯成“1”,……,25翻譯成“z”。一個數字可能有多個翻

[offer] 56. 刪除連結串列重複的結點

題目描述 在一個排序的連結串列中,存在重複的結點,請刪除該連結串列中重複的結點,重複的結點不保留,返回連結串列頭指標。 例如,連結串列1->2->3->3->4->4->5 處理後為 1->2->5 思路:  

[offer] 56. 刪除鏈表重復的結點

div new != 一個 指針 返回 span subject 保留 題目描述 在一個排序的鏈表中,存在重復的結點,請刪除該鏈表中重復的結點,重復的結點不保留,返回鏈表頭指針。 例如,鏈表1->2->3->3->4->4->5 處理後

牛客網做題總結offer題目java版

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

python刷題日記offer-調整陣列順序使奇數位於偶數前面

輸入一個整數陣列,實現一個函式來調整該陣列中數字的順序,使得所有的奇數位於陣列的前半部分,所有的偶數位於位於陣列的後半部分,並保證奇數和奇數,偶數和偶數之間的相對位置不變。 解題思路 傳統方法應該是給兩個一前一後指標,遇到前偶數與後奇數時,就交換。然而我發現一個更簡便的方法,就是給兩個列表,奇偶分開放,最後

offer 56 陣列數字出現的次數 lintcode 82. 落單的數、83. 落單的數 II、84. 落單的數 III

82. 落單的數 給出2*n + 1 個的數字,除其中一個數字之外其他每個數字均出現兩次,找到這個數字。 樣例 給出 [1,2,2,1,3,4,3],返回 4 典型思考:位運算中的異或運算 1、異或 異或運算的性質: 任何一個數字異或它自己都等於0。也就是說

牛客offer孩子們的遊戲(圓圈最後剩下的數) (Java)

轉自:http://blog.csdn.net/crazy__chen/article/details/45115911 題目描述: 解法1:找規律。首先定義最初的n個數字(0,1,…,n-1)中最後剩下的數字是關於n和m的方程為f(n,m)。在這n個數字中,第一個被刪除

offer》第二十二題(鏈表倒數第k個結點)

st3 大於 ndk 輸入 The col printf find cte // 面試題22:鏈表中倒數第k個結點 // 題目:輸入一個鏈表,輸出該鏈表中倒數第k個結點。為了符合大多數人的習慣, // 本題從1開始計數,即鏈表的尾結點是倒數第1個結點。例如一個鏈表有

offer】面試題 4.二維數組的查找

這樣的 || int targe arr find off col lean 面試題 4. 二維數組中的查找 題目:在一個二維數組中,每一行都按照從左到右遞增的順序排序,每一列都按照從上到下遞增的順序排序。 請完成一個函數,輸入這樣的一個二維數組和一個整數,判斷數組

offer》學習筆記_面試題15_二進位制1的個數

    1.解法1 int NumberOf1(int n){ int count = 0; unsigned int flag = 1; w