劍指offer(python)-記錄題解思路(41-66)

 

 

 

第四十一題 (知識遷移能力)和爲S的連續正數序列

題目描述

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

輸出描述:

輸出所有和爲S的連續正數序列。序列內按照從小至大的順序,序列間按照開始數字從小到大的順序

 

方法一

想了一個比較笨的方法

class Solution:
    def FindContinuousSequence(self, tsum):
        i = 1
        out = []
        while i <= tsum//2+1:
            sum = 0
            t = []
            j = i
            while sum < tsum:
                sum += j
                t.append(j)
                j += 1
            if sum == tsum and len(t)>1:
                out.append(t)
            i += 1
        return out

原以爲會因爲複雜度高提交失敗,沒想到過了。

 

在牛客的討論區發現兩個比較好的方法

方法二

首先是雙指針法

class Solution:
    def FindContinuousSequence(self, tsum):
        out = []
        i = 1
        j = 2
        while i < j:
            t = []
            s = (i+j)*(j-i+1)//2
            if s == tsum:
                for k in range(i, j+1):
                    t.append(k)
                out.append(t)
            if s < tsum:
                j += 1
            else:
                i += 1
        return out

方法三

參考‘丁滿歷險記’解析

1)由於我們要找的是和爲S的連續正數序列,因此這個序列是個公差爲1的等差數列,而這個序列的中間值代表了平均值的大小。假設序列長度爲n,那麼這個序列的中間值可以通過(S / n)得到,知道序列的中間值和長度,也就不難求出這段序列了。

2)滿足條件的n分兩種情況:

n爲奇數時,序列中間的數正好是序列的平均值,所以條件爲:(n & 1) == 1 && sum % n == 0;

n爲偶數時,序列中間兩個數的平均值是序列的平均值,而這個平均值的小數部分爲0.5,所以條件爲:(sum % n) * 2 == n.

3)由題可知n >= 2,那麼n的最大值是多少呢?我們完全可以將n從2到S全部遍歷一次,但是大部分遍歷是不必要的。爲了讓n儘可能大,我們讓序列從1開始,

根據等差數列的求和公式:S = (1 + n) * n / 2,得到.

class Solution:
    def FindContinuousSequence(self, tsum):
        out = []
        n = int((2*tsum)**0.5)
        for i in range(n, 1,-1):
            if (i & 1 == 1 and tsum % i == 0) or ((tsum % i) * 2 == i):
                # 求出滿足條件的i
                temp = []
                k = int((tsum / i) - (i - 1) / 2)
                for j in range(i):
                    temp.append(k)
                    k += 1
                out.append(temp)
        return out

這個方法還是要好好理解一下的

 

第四十二題 (知識遷移能力)和爲S的兩個數字

題目描述

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

輸出描述:

對應每個測試案例,輸出兩個數,小的先輸出。

 

雙指針法

class Solution:
    def FindNumbersWithSum(self, array, tsum):
        out =[]
        if not array:
            return out
        i = 0
        j = len(array)-1
        mins = array[j]*array[j]
        while i < j:
            if array[i]+array[j] == tsum:
                temp = array[i]*array[j]
                if temp < mins:
                    mins = temp
                    out.append(array[i])
                    out.append(array[j])
                i += 1
                j -= 1
            elif array[i]+array[j] < tsum: 
                i += 1
            elif array[i]+array[j] > tsum: 
                j -= 1
        return out

 

根據‘馬客(Mark)’說的:找到的第一組(相差最大的)就是乘積最小的。

證明:考慮x+y=C(C是常數),x*y的大小。不妨設y>=x,y-x=d>=0,即y=x+d, 2x+d=C, x=(C-d)/2, x*y=x(x+d)=(C-d)(C+d)/4=(C^2-d^2)/4,也就是x*y是一個關於變量d的二次函數,對稱軸是y軸,開口向下。d是>=0的,d越大, x*y也就越小。

所以其實代碼中設置的mins並沒有什麼用,去掉也不會影響最後的結果。

 

第四十三題 (知識遷移能力)左旋轉字符串

題目描述

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

 

使用切片

class Solution:
    def LeftRotateString(self, s, n):
        if not s:
            return ""
        n = n %(len(s))
        return s[n:]+s[:n]

也可以使用三次反轉

class Solution:
    def LeftRotateString(self, s, n):
        if not s: return s
        s = list(s)
        self.reverse(s, 0, n - 1)
        self.reverse(s, n, len(s) - 1)
        self.reverse(s, 0, len(s) - 1)
        return ''.join(s)

    def reverse(self, s, start, end):
        while start < end:
            s[start], s[end] = s[end], s[start]
            start += 1
            end -= 1

 

第四十四題 (知識遷移能力)翻轉單詞順序列

題目描述

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

 

class Solution:
    def ReverseSentence(self, s):
        s = list(map(str,s.split(" ")))
        s.reverse()
        return " ".join(s)

 


附:推薦工程師死絕的世界

插一句:2019.1.22

今天參加了牛客網的寒假算法訓練營(一),做到自閉,我怕是對自己有什麼誤解。

另外呢,日本IT求職學習服務平臺PAIZA推出了一個編程遊戲《工程師死絕的世界

玩了幾局感覺可以,推薦一下,看懂全靠Google翻譯。

 


 

第四十五題 (抽象建模能力)撲克牌順子

題目描述

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。

 

思路:數字0可以代表任意數

有以下幾個條件必須滿足

1、除0以外,不能有其他重複數字,注意0最多隻有4個。

2、滿足順子的話,排序後前一個數比後一個數小1。同時需要考慮有0的情況,如果後面一個數比前面一個數大於1以上,那麼就用0來補。

class Solution:
    def IsContinuous(self, numbers):
        if not numbers:
            return None
        zero = numbers.count(0)
        if zero == 5:
            return False
        numbers.sort()
        for i in range(0, len(numbers)-1):
            if numbers[i] != 0:
                if numbers[i] == numbers[i+1]:
                    return False
                zero -= numbers[i+1] - numbers[i] - 1
        if zero < 0:
            return False
        return True

 

 

第四十六題 (N)(抽象建模能力)孩子們的遊戲(圓圈中最後剩下的數)

題目描述

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

 

看着挺簡單的,但是不知道怎麼用算法表示出來

class Solution:
    def LastRemaining_Solution(self, n, m):
        if n < 1:
            return -1
        if n == 1:
            return 0 
        l = []
        for i in range(n):
            l.append(i)
        i = -1
        j = 0
        while l:
            k = (j+m-1)%n
            i = l.pop(k)
            n -= 1
            j = k
        return i

參考:約瑟夫環問題

時間複雜度爲O(n),空間複雜度爲O(1)

在這n個數字中,第一個被刪除的數字是(m-1)%n,爲簡單起見記爲k。那麼刪除k之後的剩下n-1的數字爲0,1,…,k-1,k+1,…,n-1,並且下一個開始計數的數字是k+1。相當於在剩下的序列中,k+1排到最前面,從而形成序列k+1,…,n-1,0,…k-1。該序列最後剩下的數字也應該是關於n和m的函數。由於這個序列的規律和前面最初的序列不一樣(最初的序列是從0開始的連續序列),因此該函數不同於前面函數,記爲f’(n-1,m)。最初序列最後剩下的數字f(n,m)一定是剩下序列的最後剩下數字f’(n-1,m),所以f(n,m)=f’(n-1,m)。

接下來我們把剩下的的這n-1個數字的序列k+1,…,n-1,0,…k-1作一個映射,映射的結果是形成一個從0到n-2的序列:

k+1    ->    0
k+2    ->    1

n-1    ->    n-k-2
0   ->    n-k-1

k-1   ->   n-2

把映射定義爲p,則p(x)= (x-k-1)%n,即如果映射前的數字是x,則映射後的數字是(x-k-1)%n。對應的逆映射是p-1(x)=(x+k+1)%n。

由於映射之後的序列和最初的序列有同樣的形式,都是從0開始的連續序列,因此仍然可以用函數f來表示,記爲f(n-1,m)。根據我們的映射規則,映射之前的序列最後剩下的數字f’(n-1,m)= p-1 [f(n-1,m)]=[f(n-1,m)+k+1]%n。把k=(m-1)%n代入得到f(n,m)=f’(n-1,m)=[f(n-1,m)+m]%n。

經過上面複雜的分析,我們終於找到一個遞歸的公式:f(n,m)= [f(n-1,m)+m]%n  


19.1.23  說實話沒怎麼看明白,先這樣,等明天腦子清醒了再說。

 

第四十七題 (發散思維能力)求1+2+3+...+n

題目描述

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

 

class Solution:
    def Sum_Solution(self, n):
        return (1+n)*n/2

 

第四十八題(N) (發散思維能力)不用加減乘除做加法

題目描述

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

 

參考牛客討論

明天補充


我的自言自語:第一個想法是換成二進制,然後用最原始的方法做,用i,j分別表示兩個數的同一位,t表示進位

i=0 and j = 0 看進位爲0還是1來確定相加後爲0還是1

i= 1 and j = 1 看進位爲0還是1來確定相加後爲1還是0,同時進位t=1

剩下的一樣

太複雜了,只實現了對正數的相加。


 

插一句:2019.1.24

今天參加了牛客網的寒假算法訓練營(二),還是兩道題告終

 

 

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