牛客網劍指offer——python實現(更新15題)

1.斐波那契數列

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

循環實現,時間複雜度n

def Fibonacci(self, n):
        if n == 0:
            return 0
        if n == 1:
            return 1
        a = 1
        b = 0
        ret = 0
        for i in range(0, n-1):	#[0,n-1)
            ret = a + b
            b = a
            a = ret
        return ret

遞歸實現,時間複雜度2^n

def Fibonacci(self, n):
        if n == 0:
            return 0
        if n == 1:
            return 1
        if n > 1:
            num = self.Fibonacci(n-1) + self.Fibonacci(n-2)
            return num

2.跳臺階

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

a.假定第一次跳的是一階,那麼剩下的是n-1個臺階,跳法是f(n-1)
b.假定第一次跳的是2階,那麼剩下的是n-2個臺階,跳法是f(n-2)
c.由a和b假設可以得出總跳法爲: f(n) = f(n-1) + f(n-2)
d.然後通過實際的情況可以得出:只有一階的時候 f(1) = 1,只有兩階的時候可以有 f(2) = 2
e.可以發現最終得出的是一個斐波那契數列

def jumpFloor(self, number):
        # write code here
        if number == 1:
            return 1
        a = 1
        b = 1
        for i in range(1, number):
            ret = a + b
            b = a
            a = ret
        return ret

3.變態跳臺階

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

思路1: 每個臺階可以看作一塊木板,讓青蛙跳上去,n個臺階就有n塊木板,最後一塊木板是青蛙到達的位子, 必須存在,其他 (n-1) 塊木板可以任意選擇是否存在,則每個木板有存在和不存在兩種選擇,(n-1) 塊木板 就有 [2^(n-1)] 種跳法,可以直接得到結果

思路2: 因爲n級臺階,第一步有n種跳法:跳1級、跳2級、到跳n級
跳1級,剩下n-1級,則剩下跳法是f(n-1)
跳2級,剩下n-2級,則剩下跳法是f(n-2)
跳n級,剩下n-2級,則剩下跳法是f(0)
所以f(n)=f(n-1)+f(n-2)+…+f(1)+f(0)
因爲f(n-1)=f(n-2)+f(n-3)+…+f(1)+f(0)
所以f(n)=2*f(n-1)

def jumpFloorII(self, number):
        # write code here
        return pow(2,number-1)

4.二維數組中的查找

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

從右上角的數開始找:
大於則向下,第一行的數無需對比
小於則向左,最後一列的數無需對比

def Find(self, target, array):
        # write code here
        rows = len(array)
        cols = len(array[0])
        if rows >0 and cols >0:
            i=0
            j= cols - 1
            while i < rows and j >= 0:
                value = array[i][j]
                if value == target:
                    return True
                elif value > target:
                    j -= 1
                else:
                    i += 1
        return False

5.替換空格

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

# -*- coding:utf-8 -*-
class Solution:
    # s 源字符串
    def replaceSpace(self, s):
        # write code here
        #方法一  一次遍歷  時間 O(n)  空間O(n)
        result = ''
        for i in s:
            if i == ' ':
                result += '%20'
            else:
                result += i
        return result
 
        # 方法二  使用內置函數   
        return s.replace(' ','%20')

6.用兩個棧實現隊列

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

第一個棧臨時保存插入的數據,當調用彈出函數的時候,如果stack2不爲空則直接彈出;爲空則把stack1中的數據全部彈出放到stack2中。這樣不會存在衝突,而且由於stack2保存的是以前的老數據,彈出一定都符合隊列的規律

class Solution:
    def __init__(self):
        self.stack1 = []
        self.stack2 = []
    def push(self, node):
        self.stack1.append(node)
    def pop(self):
        if self.stack2:
            return self.stack2.pop()
        else:
            while self.stack1:
                self.stack2.append(self.stack1.pop())
            return self.stack2.pop()

7.旋轉數組的最小數字

把一個數組最開始的若干個元素搬到數組的末尾,我們稱之爲數組的旋轉。
輸入一個非遞減排序的數組的一個旋轉,輸出旋轉數組的最小元素。
例如數組{3,4,5,1,2}爲{1,2,3,4,5}的一個旋轉,該數組的最小值爲1。
NOTE:給出的所有元素都大於0,若數組大小爲0,請返回0。

旋轉後的數組先遞增,然後突然斷層,讓後又遞增,所以,只要找到數組突然變小的那個數字即可。
二分查找法:如果中間點大於首元素,說明最小數字在後面一半,如果中間點小於尾元素,說明最小數字在前一半。依次循環。同時,當一次循環中首元素小於尾元素,說明最小值就是首元素。

但是當首元素等於尾元素等於中間值,只能在這個區域順序查找。

class Solution:
    def minNumberInRotateArray(self, rotateArray):
        # 優化後的遍歷
        if rotateArray is None:
            return None
        temp = rotateArray[0]
        for i in range(len(rotateArray) - 1):
            if rotateArray[i] > rotateArray[i+1]:
                temp = rotateArray[i+1]
                break
        return temp
       
        # binarySearch O(lg(n))
        if not rotateArray:
            return 0
        left = 0
        right = len(rotateArray) - 1
        while left <= right:
            mid = (left + right) >> 1 # (left +right)/2
            if rotateArray[mid] < rotateArray[mid-1]:
                return rotateArray[mid]
            elif rotateArray[mid] < rotateArray[right]:
                right = mid - 1
            else:
                left = mid + 1
        return 0

8.調整數組順序使奇數位於偶數前面

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

數組裏的任意相鄰兩個數必須是{奇數,偶數}的形式,任何出現{偶數,奇數}的形式,都要把兩個數交換位置。套用冒泡排序的思想,只需要將原來冒泡排序的判斷條件從比較兩個數的大小改爲判斷相鄰兩個數是否爲{奇數,偶數}的形式即可。

def reOrderArray(self, array):
        # 時間複雜度O(n),空間複雜度O(n)
        ret = []
        for i in array:
            if i % 2 == 1:
                ret.append(i)
        for i in array:
            if i % 2 == 0:
                ret.append(i)
        return ret
        # 簡化代碼
        odd,even = [],[]
        for i in array:
            odd.append(i) if i % 2 == 1 else even.append(i)
        return odd + even
		
		# 冒泡排序法,時間複雜度O(n^2)
        arrayLen = len(array)
        for i in range(arrayLen):
            for j in range(arrayLen - i - 1):
                if array[j] % 2 == 0 and array[j + 1] % 2 == 1:
                    array[j + 1],array[j] = array[j],array[j + 1]
        return array

9. 包含min函數的棧

定義棧的數據結構,請在該類型中實現一個能夠得到棧中所含最小元素的min函數(時間複雜度應爲O(1))。
注意:保證測試中不會當棧爲空的時候,對棧調用pop()或者min()或者top()方法。

def __init__(self):
        self.stack = []
        self.min_stack = []
    def push(self, node):
        self.stack.append(node)
        #如果min_stack爲空,或者當前結點值小於等於棧最後的那個值
        if not self.min_stack or node <= self.min_stack[-1]:
            self.min_stack.append(node)
    def pop(self):
        if self.stack[-1] == self.min_stack[-1]:
            self.min_stack.pop()
        self.stack.pop()
    def top(self):
        return self.stack[-1]
    def min(self):
        return self.min_stack[-1]

10.棧的壓入、彈出序列

輸入兩個整數序列,第一個序列表示棧的壓入順序,請判斷第二個序列是否可能爲該棧的彈出順序。假設壓入棧的所有數字均不相等。例如序列1,2,3,4,5是某棧的壓入順序,序列4,5,3,2,1是該壓棧序列對應的一個彈出序列,但4,3,5,1,2就不可能是該壓棧序列的彈出序列。(注意:這兩個序列的長度是相等的)

pushV壓入棧,循環判斷壓入棧的頂部和當前彈出棧的頂部數據是否相等,相等則彈出,最終棧爲空代表序列正確

def IsPopOrder(self, pushV, popV):
        # write code here
        if pushV == [] or len(pushV) != len(popV):
            return None
        stack,index = [],0
        for item in pushV:
            stack.append(item)
            while stack and stack[-1] == popV[index]:
                stack.pop()
                index += 1
        '''
        if stack == []:
            return True
        else:
            return False
        '''
        return True if stack == [] else False

11.不用加減乘除做加法

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

兩個數異或:相當於每一位相加,而不考慮進位;
兩個數相與,並左移一位:相當於求得進位;
將上述兩步的結果相加

python沒有無符號左移操作,所以需要越界檢查,加法是異或,進位是與<<1
https://blog.csdn.net/lrs1353281004/article/details/87192205

def Add(self, num1, num2):
        # write code here
        while(num2): 
           num1,num2 = (num1^num2) & 0xFFFFFFFF,((num1&num2)<<1) & 0xFFFFFFFF
        return num1 if num1<=0x7FFFFFFF else ~(num1^0xFFFFFFFF)

        # 用 ctypes 來定義 c 語言的數據類型
        import ctypes
        def c_int(v): return ctypes.c_int(v).value

        while num2 != 0:
            num1, num2 = c_int(num1 ^ num2), c_int((num1 & num2) << 1)
        return num1

12.二進制中1的個數

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

n與n-1進行按位與,最靠右的1置零,其他的高位的1沒有發生變化,每運行一次,就可以知道有一個1

負數在計算機是以補碼存在的,最高位爲1,而負數往右移,符號位不變,符號位1往右移,最終可能會出現全1的情況,導致死循環,與0xFFFFFFFF相與,可以消除負數的影響

def NumberOf1(self, n):
        # write code here
        n = 0xFFFFFFFF & n
        count = 0
        for c in str(bin(n)):
            if c == "1":
                count += 1
        return count

		# write code here
        count = 0
        for i in range(32):
            mask = 1 << i
            if n & mask != 0:
                count += 1
        return count
        
		#循環次數最少
		count = 0
        if n < 0:
            n = n & 0xffffffff
        while n:
            count += 1
            n = (n - 1) & n
        return count

13.重建二叉樹

輸入某二叉樹的前序遍歷和中序遍歷的結果,請重建出該二叉樹。假設輸入的前序遍歷和中序遍歷的結果中都不含重複的數字。例如輸入前序遍歷序列{1,2,4,7,3,5,6,8}和中序遍歷序列{4,7,2,1,5,3,8,6},則重建二叉樹並返回。

# -*- coding:utf-8 -*-
class TreeNode:
    def __init__(self, x):
        self.val = x
        self.left = None
        self.right = None
class Solution:
    # 返回構造的TreeNode根節點
    def reConstructBinaryTree(self, pre, tin):
        if not pre or not tin:
            return None
        if len(pre) != len(tin):
            return None
        # 取出pre的第一個值:根節點
        root = pre[0]
        rootNode = TreeNode(root)
        # 找到在中序遍歷中的根節點所在的索引位置
        pos = tin.index(root)
        # 中序遍歷的列表的左右節點分開切片成兩個列表
        tinLeft = tin[0:pos]
        tinRight = tin[pos + 1:]
        # 前序遍歷的列表的左右節點分開切片成兩個列表
        preLeft = pre[1:pos + 1]
        preRight = pre[pos + 1:]

        leftNode = self.reConstructBinaryTree(preLeft, tinLeft)
        rightNode = self.reConstructBinaryTree(preRight, tinRight)

        rootNode.left = leftNode
        rootNode.right = rightNode
        return rootNode

14.字符串的排列

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

# -*- coding:utf-8 -*-
class Solution:
    def Permutation(self, ss):
        if len(ss) <= 1:
            return ss
        res = set()
        # 遍歷字符串,固定第一個元素,第一個元素可以取a,b,c...,然後遞歸求解
        for i in range(len(ss)):
            for j in self.Permutation(ss[:i] + ss[i+1:]): # 依次固定了元素,其他的全排列(遞歸求解)
                res.add(ss[i] + j) # 集合添加元素的方法add(),集合添加去重(若存在重複字符,排列後會存在相同,如baa,baa)
        return sorted(res)         # sorted()能對可迭代對象進行排序,結果返回一個新的list

15.求1+2+3+…+n

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

遞歸+短路原理:邏輯與兩側爲真默認輸出後邊的真值

# -*- coding:utf-8 -*-
class Solution:
    def Sum_Solution(self, n):
        # write code here
        return n and (n + self.Sum_Solution(n - 1))
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章