leetcode面試top(1模擬)

134. 加油站

在一條環(huán)路上有 N 個加油站,其中第 i 個加油站有汽油 gas[i] 升。
你有一輛油箱容量無限的的汽車,從第 i 個加油站開往第 i+1 個加油站需要消耗汽油 cost[i] 升。你從其中的一個加油站出發(fā),開始時油箱為空。
如果你可以繞環(huán)路行駛一周,則返回出發(fā)時加油站的編號,否則返回 -1。

如果 sum(gas) < sum(cost) ,那么不可能環(huán)行一圈,這種情況下答案是 -1 。
對于加油站 i ,如果 gas[i] - cost[i] < 0 ,則不可能從這個加油站出發(fā),因?yàn)樵谇巴?i + 1 的過程中,汽油就不夠了。

class Solution:
    def canCompleteCircuit(self, gas, cost):
        n = len(gas)
        total_residue_tank, next_residue_tank = 0, 0
        starting_station = 0
        for i in range(n):
            total_residue_tank += gas[i] - cost[i]  # 所有加的油減去所有的消耗,最后剩下的油
            next_residue_tank += gas[i] - cost[i]  # 到達(dá)下一站后油箱里的油
            # 能否到達(dá)下一站
            if next_residue_tank < 0:
                # 不能到達(dá)下一站以下一站為起點(diǎn)
                starting_station = i + 1
                # 此時油箱里的油為空
                next_residue_tank = 0
        
        return starting_station if total_residue_tank >= 0 else -1

146. LRU緩存機(jī)制

設(shè)計(jì)和實(shí)現(xiàn)一個 LRU (最近最少使用) 緩存機(jī)制。它應(yīng)該支持以下操作: 獲取數(shù)據(jù) get 和 寫入數(shù)據(jù) put 。
獲取數(shù)據(jù) get(key) - 如果關(guān)鍵字 (key) 存在于緩存中,則獲取關(guān)鍵字的值(總是正數(shù)),否則返回 -1。
寫入數(shù)據(jù) put(key, value) - 如果關(guān)鍵字已經(jīng)存在,則變更其數(shù)據(jù)值;如果關(guān)鍵字不存在,則插入該組「關(guān)鍵字/值」。當(dāng)緩存容量達(dá)到上限時,它應(yīng)該在寫入新數(shù)據(jù)之前刪除最久未使用的數(shù)據(jù)值,從而為新的數(shù)據(jù)值留出空間。

實(shí)現(xiàn)一個可以存儲 key-value 形式數(shù)據(jù)的數(shù)據(jù)結(jié)構(gòu),并且可以記錄最近訪問的 key 值。首先想到的就是用字典來存儲 key-value 結(jié)構(gòu),這樣對于查找操作時間復(fù)雜度就是 O(1)O(1)。

但是因?yàn)樽值浔旧硎菬o序的,所以我們還需要一個類似于隊(duì)列的結(jié)構(gòu)來記錄訪問的先后順序,這個隊(duì)列需要支持如下幾種操作:

在末尾加入一項(xiàng)
去除最前端一項(xiàng)
將隊(duì)列中某一項(xiàng)移到末尾

# 雙向鏈表
class ListNode:
    def __init__(self, key=None, value=None):
            self.key = key
            self.value = value
            self.nex = None
            self.pre = None

class LRUCache:
    def __init__(self, capacity: int):
        self.capacity = capacity
        self.hashmap = {}
        # 新建兩個節(jié)點(diǎn) head 和 tail
        self.head = ListNode()
        self.tail = ListNode()
        # 初始化鏈表為 head <-> tail
        self.head.nex = self.tail
        self.tail.pre  = self.head
    # 因?yàn)間et與put操作都可能需要將雙向鏈表中的某個節(jié)點(diǎn)移到末尾,所以定義一個方法
    def move_to_end(self, key):
        
        node = self.hashmap[key]
        # 先將哈希表key指向的節(jié)點(diǎn)node拎出來
        node.pre.nex = node.nex
        node.nex.pre = node.pre
        # 之后將node插入到尾節(jié)點(diǎn)前
        node.pre = self.tail.pre
        node.nex = self.tail
        self.tail.pre.nex = node
        self.tail.pre = node
        
    def get(self, key: int) -> int:
        if key in self.hashmap:
            # 如果已經(jīng)在鏈表中了久把它移到末尾(變成最新訪問的)
            self.move_to_end(key)
        node = self.hashmap.get(key, -1)
        return node if node == -1 else node.value

    def put(self, key: int, value: int) -> None:
        if key in self.hashmap:
            # 如果key本身已經(jīng)在哈希表中了就不需要在鏈表中加入新的節(jié)點(diǎn)
            # 但是需要更新字典該值對應(yīng)節(jié)點(diǎn)的value
            self.hashmap[key].value = value
            self.move_to_end(key)
        else:
            if len(self.hashmap) == self.capacity:
                # 去掉哈希表對應(yīng)項(xiàng)
                self.hashmap.pop(self.head.nex.key)
                # 去掉最久沒有被訪問過的節(jié)點(diǎn),即頭節(jié)點(diǎn)之后的節(jié)點(diǎn)
                self.head.nex = self.head.nex.nex
                self.head.nex.pre = self.head
            # 如果不在的話就插入到尾節(jié)點(diǎn)前
            new = ListNode(key, value)
            self.hashmap[key] = new
            new.pre = self.tail.pre
            new.nex = self.tail
            self.tail.pre.nex = new
            self.tail.pre = new
# Your LRUCache object will be instantiated and called as such:
# obj = LRUCache(capacity)
# param_1 = obj.get(key)
# obj.put(key,value)

202. 快樂數(shù)

定義為:對于一個正整數(shù),每一次將該數(shù)替換為它每個位置上的數(shù)字的平方和,然后重復(fù)這個過程直到這個數(shù)變?yōu)?1,也可能是 無限循環(huán) 但始終變不到 1。如果 可以變?yōu)? 1,那么這個數(shù)就是快樂數(shù)。

最終會得到 1。
最終會進(jìn)入循環(huán)。
值會越來越大,最后接近無窮大。

Digits Largest Next
1 9 81
2 99 162
3 999 243
4 9999 324
13 9999999999999 1053

對于 3 位數(shù)的數(shù)字,它不可能大于 243。這意味著它要么被困在243 以下的循環(huán)內(nèi),要么跌到 1。4 位或 4 位以上的數(shù)字在每一步都會丟失一位,直到降到 3 位為止。所以我們知道,最壞的情況下,算法可能會在 243 以下的所有數(shù)字上循環(huán),然后回到它已經(jīng)到過的一個循環(huán)或者回到 11。但它不會無限期地進(jìn)行下去,所以我們排除第三種選擇。

# 檢測單鏈表是否有環(huán),用快慢指針
class Solution:
    def get_next(self, num):
        all_squre_sum = 0
        while num > 0:
            num, digit = divmod(num, 10)
            all_squre_sum += digit ** 2
        return all_squre_sum

    def isHappy(self, n: int) -> bool:
        slow_index = n
        fast_index = self.get_next(n)
        while fast_index != 1 and slow_index != fast_index: 
            slow_index = self.get_next(slow_index)
            fast_index = self.get_next(self.get_next(fast_index))
        return fast_index == 1

289. 生命游戲

給定一個包含 m × n 個格子的面板,每一個格子都可以看成是一個細(xì)胞。每個細(xì)胞都具有一個初始狀態(tài):1即為活細(xì)胞(live),或 0 即為死細(xì)胞(dead)。每個細(xì)胞與其八個相鄰位置(水平,垂直,對角線)的細(xì)胞都遵循以下四條生存定律:
1.如果活細(xì)胞周圍八個位置的活細(xì)胞數(shù)少于兩個,則該位置活細(xì)胞死亡;
2.如果活細(xì)胞周圍八個位置有兩個或三個活細(xì)胞,則該位置活細(xì)胞仍然存活;
3.如果活細(xì)胞周圍八個位置有超過三個活細(xì)胞,則該位置活細(xì)胞死亡;
4.如果死細(xì)胞周圍正好有三個活細(xì)胞,則該位置死細(xì)胞復(fù)活;

根據(jù)當(dāng)前狀態(tài),寫一個函數(shù)來計(jì)算面板上所有細(xì)胞的下一個(一次更新后的)狀態(tài)。下一個狀態(tài)是通過將上述規(guī)則同時應(yīng)用于當(dāng)前狀態(tài)下的每個細(xì)胞所形成的,其中細(xì)胞的出生和死亡是同時發(fā)生的。

如果你直接根據(jù)規(guī)則更新原始數(shù)組,那么就做不到題目中說的 同步 更新。假設(shè)你直接將更新后的細(xì)胞狀態(tài)填入原始數(shù)組,那么當(dāng)前輪次其他細(xì)胞狀態(tài)的更新就會引用到當(dāng)前輪已更新細(xì)胞的狀態(tài),但實(shí)際上每一輪更新需要依賴上一輪細(xì)胞的狀態(tài),是不能用這一輪的細(xì)胞狀態(tài)來更新的。

拓展一些復(fù)合狀態(tài)使其包含之前的狀態(tài)。

class Solution:
    def gameOfLife(self, board: List[List[int]]) -> None:
        neighbors = [(-1, -1), (-1, 0), (-1, 1), 
                     (0, -1), (0, 1), 
                     (1, -1), (1, 0), (1, 1)]
        rows, cols = len(board), len(board[0])

        # 遍歷面板每一個格子里的細(xì)胞,根據(jù)周圍細(xì)胞改變聯(lián)合狀態(tài)
        for r in range(rows):
            for c in range(cols):
                # 對于每一個細(xì)胞統(tǒng)計(jì)其八個相鄰位置里的活細(xì)胞數(shù)量
                live_neighbor = 0
                for i in neighbors:
                    if 0 <= r+i[0] < rows and 0 <= c+i[1] < cols and abs(board[r+i[0]][c+i[1]]) == 1:
                        # abs() 因?yàn)楹竺鏁?改成-1
                        live_neighbor += 1
                # 規(guī)則 2 不造成狀態(tài)變化

                # 規(guī)則 1 或規(guī)則 3  活細(xì)胞,周圍活的少于兩個多于三個,死
                if board[r][c] == 1 and (live_neighbor > 3 or live_neighbor < 2):
                    board[r][c] = -1
                # 規(guī)則 4 死細(xì)胞,周圍活的為2或3,活
                if board[r][c] == 0 and live_neighbor == 3:
                    board[r][c] = 2
                    # # 遍歷面板每一個格子里的細(xì)胞
        # 遍歷 board 得到一次更新后的狀態(tài)
        for r in range(rows):
            for c in range(cols):
                if board[r][c] > 0:
                    board[r][c] = 1
                else:
                    board[r][c] = 0

371. 兩整數(shù)之和

正數(shù)的補(bǔ)碼就是原碼,而負(fù)數(shù)的補(bǔ)碼=原碼符號位不變,其他位按位取反后+1。

不使用運(yùn)算符 + 和 - ???????,計(jì)算兩整數(shù) ???????a 、b ???????之和。

異或的一個重要特性是無進(jìn)位加法。我們來看一個例子:

a = 5 = 0101
b = 4 = 0100
a ^ b 如下:
0 1 0 1
0 1 0 0
0 0 0 1
a ^ b 得到了一個無進(jìn)位加法結(jié)果,如果要得到 a + b 的最終值,我們還要找到進(jìn)位的數(shù),把這二者相加。
在位運(yùn)算中,我們可以使用與操作獲得進(jìn)位:
a = 5 = 0101
b = 4 = 0100
a & b 如下:
0 1 0 1
0 1 0 0
0 1 0 0
由計(jì)算結(jié)果可見,0100 并不是我們想要的進(jìn)位,1 + 1 所獲得的進(jìn)位應(yīng)該要放置在它的更高位,即左側(cè)位上,因此我們還要把 0100 左移一位,才是我們所要的進(jìn)位結(jié)果。

總結(jié)一下:

  1. a + b 的問題拆分為 (a 和 b 的無進(jìn)位結(jié)果) + (a 和 b 的進(jìn)位結(jié)果)
  2. 無進(jìn)位加法使用異或運(yùn)算計(jì)算得出
  3. 進(jìn)位結(jié)果使用與運(yùn)算和移位運(yùn)算計(jì)算得出
  4. 循環(huán)此過程,直到進(jìn)位為 0

在 Python 中,整數(shù)不是 32 位的,也就是說你一直循環(huán)左移并不會存在溢出的現(xiàn)象,這就需要我們手動對 Python 中的整數(shù)進(jìn)行處理,手動模擬 32 位 INT 整型。

a=-2 ; b=3
a=1 1 1 0
b=0 0 1 1

  1. 將輸入數(shù)字轉(zhuǎn)化成無符號整數(shù)
    a &= 0xF # a = a & 0b1111 = 1110
    b &= 0xF # b = 0011
  2. 計(jì)算無符號整數(shù)相加并的到結(jié)果
while b:
    carry = a & b
    a ^= b
    b = carry << 1 & 0xF # 模擬溢出操作

過程為

0 1 2 3
carry 0010 0100 1000
a 1110 1101 1001 0001
b 0011 0100 1000 0000

最后結(jié)果為 1 是沒有問題的
但是對于 -2 + -2 , 最后結(jié)果為 1110 + 1110 = 1100 (12) 會出現(xiàn)問題

  1. 講結(jié)果根據(jù)范圍判定,映射為有符號整型
    首先有符號整數(shù)的值域應(yīng)該為 [-8, 7] 對于初步運(yùn)算的結(jié)果,當(dāng)結(jié)果小于8直接返回就可.
    對于大于 7 的結(jié)果, 可知符號位必為1. 現(xiàn)在的問題轉(zhuǎn)化為, 如何通過位運(yùn)算把負(fù)數(shù)轉(zhuǎn)換出來.
    假設(shè)python用的是 8bit 有符號整數(shù),當(dāng)前結(jié)果為0000 1100, 對應(yīng)8bit有符號整數(shù)為12, 但結(jié)果應(yīng)該為-4對應(yīng)8bit有符號整數(shù)為1111 1100
    通過兩步轉(zhuǎn)換可以得到:
    1.結(jié)果 與 0b1111 異或
    2.對異或結(jié)果按位取反
    ~(a ^ 0xF)
# 異或  - -> 無進(jìn)位加法
# 與,向左移動一位 - - > 進(jìn)位
class Solution:
    def getSum(self, a: int, b: int) -> int:
        # 截取后32位為無符號整數(shù)
        a &= 0xFFFFFFFF
        b &= 0xFFFFFFFF
        # 無符號整數(shù)加法
        while b:
            # 模擬進(jìn)位
            carry = (a & b) << 1
            # 模模擬相加
            a ^= b
            b = carry & 0xFFFFFFFF  # 模擬溢出操作
        # 結(jié)果映射為有符號整數(shù) 
        return a if a < 0x80000000 else ~(a ^ 0xFFFFFFFF)

412. Fizz Buzz

寫一個程序,輸出從 1 到 n 數(shù)字的字符串表示。

  1. 如果 n 是3的倍數(shù),輸出“Fizz”;
  2. 如果 n 是5的倍數(shù),輸出“Buzz”;
    3.如果 n 同時是3和5的倍數(shù),輸出 “FizzBuzz”。
class Solution:
    def fizzBuzz(self, n: int) -> List[str]:
        result_list = []
        # 映射關(guān)系放在散列表,可以對散列表添加/刪除映射關(guān)系 
        key_value = {3: 'Fizz', 5: 'Buzz'}
        for i in range(1, n+1):
            result = ''
            for k in key_value:
                if i % k == 0:
                    result += key_value[k]
            if not result:
                result += str(i)
            result_list.append(result)
        return result_list

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容