LEETCODE-刷題個人筆記 Python(1-400)-TAG標籤版本(二)

前面一篇由於文字太多,不給編輯,遂此篇出爐
LEETCODE-刷題個人筆記 Python(1-400)-TAG標籤版本(一)

DFS&BFS

200. Number of Islands(Medium)

給定1d(陸地)和’0’(水)的2d網格圖,計算島嶼數量。島被水包圍,通過水平或垂直連接相鄰的土地而形成。
您可以假設網格的所有四個邊都被水包圍。
在這裏插入圖片描述

思路:
1、使用dfs
2、如果此處是1的話,就把周邊所有1 換成X,防止再次訪問(多個相鄰的1只能當作一個島嶼)

    def numIslands(self, grid):
        """
        :type grid: List[List[str]]
        :rtype: int
        """
        def sink(i, j):
            if 0 <= i < len(grid) and 0 <= j < len(grid[i]) and grid[i][j] == '1':
                grid[i][j] = 'X'
                map(sink, (i+1, i-1, i, i), (j, j, j+1, j-1))
                return 1
            return 0
        
        return sum(sink(i, j) for i in range(len(grid)) for j in range(len(grid[i])))

286 Walls and Gates(Medium)

您將獲得使用這三個可能值初始化的m x n 2D網格。
-1 - 牆壁或障礙物。
0 - 門
INF - Infinity意味着一個空房間。
每個空房間填充距離最近的門的距離。如果無法到達大門,則應填充INF。
在這裏插入圖片描述

思路:
只有噹噹前值大於上一值+1時,我們才遞歸,從而更新;其他情況我們不需要更新,所以也不需要遞歸

def wallsAndGates(rooms):
    def dfs(x,y):
        for i,j in [[-1,0],[1,0],[0,-1],[0,1]]:
            if 0<=i+x<m and 0<=j+y<n and rooms[x+i][y+j]>0:
                if rooms[x+i][y+j]>rooms[x][y]+1:
                    rooms[x+i][y+j] = rooms[x][y]+1
                    dfs(x+i,y+j)

    if not rooms:
        print(rooms)
    m = len(rooms)
    n = len(rooms[0])
    for i in range(m):
        for j in range(n):
            if rooms[i][j]==0:
                dfs(i,j)

130. Surrounded Regions(Medium)

給定包含“X”和“O”(字母O)的2D板,捕獲由“X”包圍的所有區域。 通過將所有’O’翻轉到該周圍區域中的’X’來捕獲區域。
在這裏插入圖片描述

思路:
1、先看邊界,邊界有O加入stack中
2、然後取出stack中的元素,改爲S,如果其上下左右有O,則加入stack,直到stack中沒有點爲止
3、最後將2D板中不是S的元素全部改爲X

def solve(self, board):
    """
    :type board: List[List[str]]
    :rtype: None Do not return anything, modify board in-place instead.
    """
    m = len(board)
    if m==0:
        return
    n = len(board[0])
    stack = []
    for i in range(n):
        if board[0][i]=='O':
            stack.append([0,i])
        if board[m-1][i]=='O':
            stack.append([m-1,i])
    for i in range(1,m-1):
        if board[i][0] =='O':
            stack.append([i,0])
        if board[i][n-1]=='O':
            stack.append([i,n-1])
    while stack:
        pos = stack.pop(0)
        board[pos[0]][pos[1]] = 'S'
        if pos[0]>0 and board[pos[0]-1][pos[1]] =='O':
            stack.append([pos[0]-1,pos[1]])
        if pos[0]<m-1 and board[pos[0]+1][pos[1]] =='O':
            stack.append([pos[0]+1,pos[1]])
        if pos[1]>0 and board[pos[0]][pos[1]-1] =='O':
            stack.append([pos[0],pos[1]-1])
        if pos[1]<n-1 and board[pos[0]][pos[1]+1] =='O':
            stack.append([pos[0],pos[1]+1])
            
    for i in range(m):
        for j in range(n):
            if board[i][j]=='S':
                board[i][j]='O'
            else:
                board[i][j]='X'

339 Nested List Weight Sum(Easy)

給定嵌套的整數列表,返回列表中所有整數的總和,加權深度。 每個元素都是整數或列表 - 其元素也可以是整數或其他列表。
Example 1:
Given the list [[1,1],2,[1,1]], return 10. (four 1’s at depth 2, one 2 at depth 1)
Example 2:
Given the list [1,[4,[6]]], return 27. (one 1 at depth 1, one 4 at depth 2, and one 6 at depth 3; 1 + 42 + 63 = 27)

思路:
嵌套整數鏈表的加權求和。這個結構裏面有整數也有鏈表,整數的話就乘上深度返回,而是鏈表的話就進入下一層繼續遍歷。

我們可以很容易的由嵌套想到遞歸解決這個問題,類似於深度優先遍歷。
學會使用isinstance

class Solution:

    def depthSum(self,nestedList):
        self.res = 0
        def dfs(arr, depth):
            for i in arr:
                if isinstance(i,int):
                    self.res +=i*depth
                else:
                    dfs(i,depth+1)

        dfs(nestedList,1)
        print(self.res)

a = Solution()
a.depthSum([[1,1],2,[1,1]])

364 Nested List Weight Sum II(Medium)

嵌套整數鏈表的加權求和。這個結構裏面有整數也有鏈表,整數的話就乘上深度返回,而是鏈表的話就進入下一層繼續遍歷。
與之前的問題不同,重量從根到葉增加,現在權重是從下到上定義的。即,葉級整數具有權重1,並且根級整數具有最大權重。
在這裏插入圖片描述
思路:
先記錄下來每一層的元素,最後遞歸出來之後在相加乘以權重

def depthSumInverse(nestedList):
	int sum = 0
	res = []
	dfs(nestedList,0,res)
	for i in range(1,len(res)+1):
		sum +=i*res[len(res)-i]
	return sum

def dfs(nestedList,depth,res):
	if depth == len(res):
		res.append([0])
	for a in nestedList:
		if isinstance(int,a):
			res[depth] += a
		else:
			dfs(nestedList,depth+1,res)

126. Word Ladder II(Hard)

給定兩個單詞(beginWord和endWord)和一個字典的單詞列表,找到從beginWord到endWord的所有最短變換序列
Input:
beginWord = “hit”,
endWord = “cog”,
wordList = [“hot”,“dot”,“dog”,“lot”,“log”,“cog”]
Output:
[
[“hit”,“hot”,“dot”,“dog”,“cog”],
[“hit”,“hot”,“lot”,“log”,“cog”]
]

思路:
難題,據說是通過率最小的題

使用dfs一個個找和某個單詞相關的(即相差一個字符的)然後存入字典,每次更新字典
1、使用new_layer來記錄下一層要用的
2、將所有隻差一個字母的存到new_layer中,將new_layer賦值給layer

    def findLadders(self, beginWord, endWord, wordList):
        """
        :type beginWord: str
        :type endWord: str
        :type wordList: List[str]
        :rtype: List[List[str]]
        """
        wordList  = set(wordList)
        res = []
        layer = {}
        layer[beginWord] = [[beginWord]]
        #如果endWord不在List中,則直接退出
        if endWord not in wordList:
            return res
        while(layer):
            #用一個新的層來記錄下一步的layer
            new_layer = collections.defaultdict(list)
            for w in layer:
                if w ==endWord:
                    res.extend(i for i in layer[w])
                else:
                    #將所有和w只差一個字母的存起來
                    for i in range(len(w)):
                        for c in 'abcdefghijklmnopqrstuvwxyz':
                            w1 = w[:i]+c+w[i+1:]
                            #如果在list找到了,就添加到new_layer中
                            if w1 in wordList:
                                new_layer[w1] += [j+[w1] for j in layer[w]]
            #將所有已經用過的詞刪除(找出最短的)
            wordList -=set(new_layer.keys())
            layer = new_layer
            print(layer)
        
        return res

127. Word Ladder(Medium)

給定兩個單詞(beginWord和endWord)和一個字典的單詞列表,找到從beginWord到endWord的最短變換序列的長度

思路:只會用和126一樣的方式,不過運行速度有點慢

    def ladderLength(self, beginWord, endWord, wordList):
        """
        :type beginWord: str
        :type endWord: str
        :type wordList: List[str]
        :rtype: int
        """
        wordList  = set(wordList)
        res = []
        layer = {}
        layer[beginWord] = [[beginWord]]
        if endWord not in wordList:
            return 0
        while(layer):
            new_layer = collections.defaultdict(list)
            for w in layer:
                if w ==endWord:
                    res.extend(i for i in layer[w])
                    return len(res[0])
                else:
                    for i in range(len(w)):
                        for c in 'abcdefghijklmnopqrstuvwxyz':
                            w1 = w[:i]+c+w[i+1:]
                            if w1 in wordList:
                                new_layer[w1] += [j+[w1] for j in layer[w]]
            wordList -=set(new_layer.keys())
            layer = new_layer
        
        return 0

51 52. N-Queens(Hard)

思路:用DFS實現,重要的是定義一個index,循環一次nums[index] == i
還有一個判斷條件,斜對角絕對值之差!=index-i
定義一個1維長度爲n的向量,用來存儲列的值

51. N-Queens(Hard)

給定整數n,返回n-queens拼圖的所有不同解。

思路:(只需要一維的向量)
1、(判斷斜對角沒有那麼簡單)abs(nums[i]-nums[n])==n-i
2、可以每一行的添加元素,這樣就只需要一個循環,而且不用判斷每一行有沒有一樣的
3、可以使用遞歸來做

    def solveNQueens(self, n):
        """
        :type n: int
        :rtype: List[List[str]]
        """
        def is_vaild(nums,n):
            for i in range(n):
                if abs(nums[i]-nums[n])==n-i or nums[i]==nums[n]:
                    return False
            return True
                
        def dfs(nums,path,index):
            if len(nums)==index:
                res.append(path)
                return
            for i in range(len(nums)):
                nums[index] = i
                if is_vaild(nums,index):
                    temp = '.'*len(nums)
                    dfs(nums,path+[temp[:i]+'Q'+temp[i+1:]],index+1)
                    
            
            
        nums = [0]*n
        res = []
        dfs(nums,[],0)
        return res

52. N-Queens II(Hard)

給定整數n,返回n-queens拼圖的不同解的數量。

思路:和51的思路一樣,但是無需存儲路徑

    def totalNQueens(self, n):
        """
        :type n: int
        :rtype: int
        """
        def is_vaild(nums,index):
            for i in range(index):
                if abs(nums[i]-nums[index])==index-i or nums[i]==nums[index]:
                    return False
            return True
        def dfs(nums,index):
            if index ==len(nums):
                self.res +=1
                return
            for i in range(len(nums)):
                nums[index] = i
                if is_vaild(nums,index):
                    dfs(nums,index+1)
        nums = [0]*n
        self.res = 0
        dfs(nums,0)
        return self.res

Stack & PriorityQueue

155. Min Stack(Easy)

設計一個支持push,pop,top和在恆定時間內檢索最小元素的堆棧。
push(x) - 將元素x推入堆棧。
pop() - 刪除堆棧頂部的元素。
top() - 獲取頂部元素。
getMin() - 檢索堆棧中的最小元素。
在這裏插入圖片描述

思路:
每次存的時候,同時看一下最小值是哪一個,並把x和最小值同時存儲

class MinStack:

    def __init__(self):
        """
        initialize your data structure here.
        """
        self.q = []
        

    def push(self, x):
        """
        :type x: int
        :rtype: void
        """
        curmin = self.getMin()
        if curmin==None or curmin>x:
            curmin = x
        self.q.append([x,curmin])
        

    def pop(self):
        """
        :rtype: void
        """
        return self.q.pop()
        

    def top(self):
        """
        :rtype: int
        """
        if len(self.q)==0:
            return None
        else:
            return self.q[-1][0]
        
        

    def getMin(self):
        """
        :rtype: int
        """
        if len(self.q)==0:
            return None
        else:
            return self.q[-1][1]

232 Implement Queue using Stacks(Easy)

使用堆棧實現隊列的以下操作。
push(x) - 將元素x推送到隊列的後面。
pop() - 從隊列前面刪除元素。
peek() - 獲取前面的元素。
empty() - 返回隊列是否爲空。
在這裏插入圖片描述

思路:
使用兩個stack,一個用來存儲,一個用來pop,當s2爲空的時候,把s1中的結果放入s2中。加入元素的時候把元素加入s1中。

class MyQueue:
    def __init__(self):
        self.s1 = []
        self.s2 = []

    def push(self, x):
        self.s1.append(x)

    def pop(self):
        self.peek()
        return self.s2.pop()

    def peek(self):
        if not self.s2:
            while self.s1:
                self.s2.append(self.s1.pop())
        return self.s2[-1]        

    def empty(self):
        return not self.s1 and not self.s2

225 Implement Stack using Queues(Easy)

使用隊列實現堆棧的以下操作。
push(x) - 將元素x推入堆棧。
pop() - 刪除堆棧頂部的元素。
top() - 獲取頂部元素。
empty() - 返回堆棧是否爲空。

思路:
只用使用一個queue就行,在放進一個元素之後,其它元素再往後面放

class MyStack(object):

    def __init__(self):
        """
        Initialize your data structure here.
        """
        self.q =collections.deque()

    def push(self, x):
        """
        Push element x onto stack.
        :type x: int
        :rtype: None
        """
        self.q.append(x)
        size = len(self.q)
        while size>1:
            self.q.append(self.q.pop())
            size -=1
            

    def pop(self):
        """
        Removes the element on top of the stack and returns that element.
        :rtype: int
        """
        return self.q.pop()
        

    def top(self):
        """
        Get the top element.
        :rtype: int
        """
        val = self.q.pop()
        self.push(val)
        return val
        

    def empty(self):
        """
        Returns whether the stack is empty.
        :rtype: bool
        """
        return len(self.q)==0

71. Simplify Path(Medium)

給定文件的絕對路徑(Unix風格),簡化它。或者換句話說,將其轉換爲規範路徑。
請注意,返回的規範路徑必須始終以斜槓/開頭,並且兩個目錄名之間必須只有一個斜槓/。最後一個目錄名稱(如果存在)不得以尾隨/結尾。
Input: “/a//b////c/d//././/…”
Output: “/a/b/c”

先將絕對路徑按照’/‘分開,然後利用stack

    def simplifyPath(self, path):
        """
        :type path: str
        :rtype: str
        """
        p = [i for i in path.split('/') if i!='' and i!='.']
        res =[]
        for i in p:
            if i =='..':
                if res ==[]:
                    continue
                else:
                    res.pop()
            else:
                res.append(i)
        return '/'+'/'.join(res)

388 Longest Absolute File Path(Medium)(未寫)

394 Decode String(Medium)(未寫)

224 Basic Calculator(Hard)(未寫)

227 Basic Calculator II(Medium)(未寫)

385 Mini Parser(Medium)(未寫)

84. Largest Rectangle in Histogram***(Hard)

給定n個非負整數表示直方圖的條形高度,其中每個條形的寬度爲1,找到直方圖中最大矩形的區域。

在這裏插入圖片描述
思路:題型和11題類似,但是解法不同。
此題用stack的思想O(n),重要的幾個trick
1、stack = [-1] height.append(0) #在最後面添加一個最小數
2、循環當矩陣不是遞增的時候,彈出末尾的元素,然後算面積。否則stack.append(i)(注:是加入索引)

用棧來模擬,遍歷heights數組,如果大於棧頂元素,就push進去;否則,持續彈棧來計算從棧頂點到降序點的矩陣大小。然後將這一部分全部替換爲降序點的值,即做到了整體依然是有序非降的。
整個過程中,即把所有的局部最大矩陣計算過了,又在寬度範圍內保留了全部的場景。
舉例,2,1,5,6,3的場景。
先加入一個0,方便最後可以全部彈棧出來。變成:2,1,5,6,3,0.
2進棧,1比棧頂小,對2進行出棧,area = 2;
5,6都是非降的,繼續進棧,棧爲1,5,6;
遇到3,是一個降序點;開始彈棧,6出棧,對應area=61; 5出棧對應area=52;下一個1比3小,不需要彈棧。然後將5、6的彈棧後的空位壓棧爲3,這是棧爲1,1,3,3,3;
下一步遇到0,開始依次出棧,得到area=31,32,33,14,1*5。
遍歷結束。整個過程中max的area爲10.

    def largestRectangleArea(self, height):
        #在最後面添加一個最小數
        height.append(0)
        stack = [-1]
        ans = 0
        for i in range(len(height)):
            while height[i]<height[stack[-1]]:
                h = height[stack.pop()]
                w = i - stack[-1] -1
                ans = max(ans,h*w)
                print(ans)
            stack.append(i)
        return ans

215 Kth Largest Element in an Array(Medium)(未寫)

347 Top K Frequent Elements(Medium)(未寫)

218 The Skyline Problem(Hard)(未寫)

332 Reconstruct Itinerary(Medium)(未寫)

341 Flatten Nested List Iterator(Medium)(未寫)

Bit Manipulation

389 Find the Difference(Easy)

給定兩個字符串s和t,它們只包含小寫字母。 字符串t由隨機混洗字符串s生成,然後在隨機位置再添加一個字母。 找到t中添加的字母。

思路:

    def findTheDifference(self, s, t):
        """
        :type s: str
        :type t: str
        :rtype: str
        """
        res = ord(t[-1])
        for i in range(len(s)):
            res =res+ ord(t[i]) -ord(s[i])
        return chr(res)

136. Single Number(Easy)

給定一個非空的整數數組,除了一個元素外,每個元素都會出現兩次。找一個單一的。 注意: 您的算法應具有線性運行時複雜性。你能不用額外的內存來實現嗎?
在這裏插入圖片描述

思路:
使用異或操作

    def singleNumber(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """
        a=0
        for i in nums:
            a = a^i
            print(a)
        return a

318. Maximum Product of Word Lengths(Medium)(未寫)

Topological Sort

207. Course Schedule(Medium)

您必須參加總共n門課程,標記爲0到n-1。 有些課程可能有先決條件,例如,要修課程0,你必須先修課程1,表示爲一對:[0,1]
鑑於課程總數和先決條件對列表,您是否可以完成所有課程?
在這裏插入圖片描述

思路:
此題考驗的是無環有向圖,還有拓撲排序
1、首先根據所有課程的先決條件構建無環有向圖
2、存儲所有節點的入度
3、當入度爲0時,加入pre中,然後使用循環把所有和pre中有關的節點入度都減一

    def canFinish(self, numCourses, prerequisites):
        """
        :type numCourses: int
        :type prerequisites: List[List[int]]
        :rtype: bool
        """
        graph = [[] for _ in range(numCourses)]
        degree = [0 for i in range(numCourses)]
        for i in prerequisites:
            graph[i[0]].append(i[1])
            degree[i[1]] +=1
        
        pre = []
        for i in range(len(degree)):
            if degree[i] ==0:
                pre.append(i)
        
        while(pre):
            node = pre.pop()
            for i in graph[node]:
                degree[i] -=1
                if degree[i] == 0:
                    pre.append(i)
        return max(degree)==0

210. Course Schedule II(Medium)

您必須參加總共n門課程,標記爲0到n-1。 有些課程可能有先決條件,例如,要修課程0,你必須先修課程1,表示爲一對:[0,1]
鑑於課程總數和先決條件對列表,列出按順序上的課程
在這裏插入圖片描述

思路:(和207類似)
此題考驗的是無環有向圖,還有拓撲排序
1、首先根據所有課程的先決條件構建無環有向圖
2、存儲所有節點的入度
3、當入度爲0時,加入pre中,然後使用循環把所有和pre中有關的節點入度都減一,使用一個a節點記錄所有節點爲0的節點

    def findOrder(self, numCourses, prerequisites):
        """
        :type numCourses: int
        :type prerequisites: List[List[int]]
        :rtype: List[int]
        """
        graph = [[] for _ in range(numCourses)]
        degree = [0 for i in range(numCourses)]
        for i in prerequisites:
            graph[i[1]].append(i[0])
            degree[i[0]] +=1
        
        pre = []
        a = []
        for i in range(len(degree)):
            if degree[i] ==0:
                pre.append(i)
                a.append(i)
        
        while(pre):
            node = pre.pop()
            for i in graph[node]:
                degree[i] -=1
                if degree[i] == 0:
                    pre.append(i)
                    a.append(i)
        return a if len(a)==numCourses else []

269 Alien Dictionary(Hard)(未寫)

Random

384 Shuffle an Array

398 Random Pick Index

382 Linked List Random Node

380 Insert Delete GetRandom O(1)

381 Insert Delete GetRandom O(1) - Duplicates allowed

138. Copy List with Random Pointer(Medium)

複製一個複雜鏈表,這個複雜鏈表是指出了值和next指針外,還有一個random指針可能指向任何位置的鏈表節點或空。
在這裏插入圖片描述

思路:
1、使用字典來創建類型,記得要添加一個dict[None] = None 類型
2、然後next遍歷,賦值

    def copyRandomList(self, head):
        """
        :type head: Node
        :rtype: Node
        """
        dict = collections.defaultdict(lambda:Node(0,0,0))
        dict[None] = None
        n = head
        while n:
            dict[n].val = n.val
            dict[n].next = dict[n.next]
            dict[n].random = dict[n.random]
            n = n.next
        return dict[head]

Graph

133. Clone Graph(Medium)

給定連接的無向圖中的節點的引用,返回圖的深拷貝(克隆)。圖中的每個節點都包含其鄰居的val(int)和列表(List [Node])。
在這裏插入圖片描述

思路:
1、使用DFS遍歷整個網絡
2、如果存在該節點,則直接返回該節點的指針
3、不存在則創建Node,然後遍歷其neighbors

    def cloneGraph(self, node):
        """
        :type node: Node
        :rtype: Node
        """
        if node == None:
            return None
        d = {}
        
        def dfs(node):
            if node in d:
                return d[node]
            ans = Node(node.val,[])
            d[node] = ans
            for i in node.neighbors:
                ans.neighbors.append(dfs(i))
            return ans
        return dfs(node)

399 Evaluate Division

310 Minimum Height Tree

149. Max Points on a Line(Hard)(很少考)

給定2D平面上的n個點,找到位於同一直線上的最大點數。
在這裏插入圖片描述

思路:
首先他們滿足在同一直線上,即a = (x1-x0)/(y1 - y0) = (x2-x1)/(y2-y1)
1、使用雙層循環進行判斷,定義一個全局變量m來存儲當前最大
2、首先判斷兩個點是不是重疊,是的話,same+1
3、定義一個字典,用來存儲不同a對應的數量,找出最大

from fractions import Fraction
class Solution(object):
    def maxPoints(self, points):
        """
        :type points: List[List[int]]
        :rtype: int
        """
        m = 0
        for i in range(len(points)):
            if m>=len(points)-i:
                break
            dict = {'i':1}
            same = 0
            for j in range(i+1,len(points)):
                if points[i][0] ==points[j][0] and points[j][1]==points[i][1]:
                    same +=1
                    continue
                if points[i][0] == points[j][0]:
                    slop = i
                else:
                    slop = Fraction((points[j][1]-points[i][1]),(points[j][0] - points[i][0]))
                if slop not in dict:
                    dict[slop] = 1
                dict[slop] +=1
            m = max(m,max(dict.values())+same)
        return m

Union FInd

261 Graph Valid Tree

323 Number of Connected Components in an Undirected Graph

305 Number of Islands II

Trie

208. Implement Trie (Prefix Tree)(Medium)

使用insert,search和startsWith方法實現trie。
在這裏插入圖片描述

思路:
定義一個TrieNode,包含word(判斷這個word是不是在trie中)、children存儲這個字的每個字符

class TrieNode:
    def __init__(self):
        self.word = False
        self.children = {}

class Trie(object):

    def __init__(self):
        """
        Initialize your data structure here.
        """
        self.root = TrieNode()

        

    def insert(self, word):
        """
        Inserts a word into the trie.
        :type word: str
        :rtype: None
        """
        node = self.root
        for i in word:
            if i not in node.children:
                node.children[i] = TrieNode()
            node = node.children[i]
        node.word = True
        

    def search(self, word):
        """
        Returns if the word is in the trie.
        :type word: str
        :rtype: bool
        """
        node = self.root
        for i in word:
            if i not in node.children:
                return False
            node = node.children[i]
        return node.word

    def startsWith(self, prefix):
        """
        Returns if there is any word in the trie that starts with the given prefix.
        :type prefix: str
        :rtype: bool
        """
        node = self.root
        for i in prefix:
            if i not in node.children:
                return False
            node = node.children[i]
        return True

211. Add and Search Word - Data structure design(Medium)

在這裏插入圖片描述

思路:
1、初始化使用list dict
2、通過字的長度進行存儲 word
3、使用雙循環判斷word在哪個裏面

class WordDictionary(object):

    def __init__(self):
        """
        Initialize your data structure here.
        """
        self.word_dict = collections.defaultdict(list)
        

    def addWord(self, word):
        """
        Adds a word into the data structure.
        :type word: str
        :rtype: None
        """
        if word:
            self.word_dict[len(word)].append(word)

    def search(self, word):
        """
        Returns if the word is in the data structure. A word could contain the dot character '.' to represent any one letter.
        :type word: str
        :rtype: bool
        """
        if not word:
            return False
        if '.' not in word:
            return word in self.word_dict[len(word)]
        for v in self.word_dict[len(word)]:
            success = True
            for i,char in enumerate(word):
                if char!=v[i] and char!='.':
                    success = False
                    break
        return success


# Your WordDictionary object will be instantiated and called as such:
# obj = WordDictionary()
# obj.addWord(word)
# param_2 = obj.search(word)

212. Word Search II(Hard)

給定2D板和字典中的單詞列表,找到板中的所有單詞。
每個字必須由順序相鄰的單元的字母構成,其中“相鄰”單元是水平或垂直相鄰的單元。一個單詞中不能多次使用相同的字母單元格。
在這裏插入圖片描述

思路:
1、使用字典樹Trie,把所有要找的單詞放在字典樹裏,然後每個元素上下左右搜索,找到了就結束當前分支的搜索

class TrieNode():
    def __init__(self):
        self.children = collections.defaultdict(TrieNode)
        self.isWord = False

class Trie():
    def __init__(self):
        self.root = TrieNode()
    def insert(self,word):
        node = self.root
        for w in word:
            if w not in node.children:
                node.children[w] = TrieNode()
            node = node.children[w]
        node.isWord = True
    def search(self,word):
        node = self.root
        for w in word:
            node = node.children.get(w)
            if not node:
                return False
        return node.isWord

class Solution(object):
    def findWords(self, board, words):
        """
        :type board: List[List[str]]
        :type words: List[str]
        :rtype: List[str]
        """
        res = []
        trie = Trie()
        node = trie.root
        for w in words:
            trie.insert(w)
        for i in range(len(board)):
            for j in range(len(board[0])):
                self.dfs(board,node,i,j,'',res)
        return res
    
    def dfs(self,board,node,i,j,path,res):
        if node.isWord:
            res.append(path)
            node.isWord = False
        if i<0 or i>=len(board) or j<0 or j>=len(board[0]):
            return
        
        temp = board[i][j]
        node = node.children.get(temp)
        if not node:
            return
        board[i][j] = '#'
        self.dfs(board,node,i+1,j,path+temp,res)
        self.dfs(board,node,i,j+1,path+temp,res)
        self.dfs(board,node,i-1,j,path+temp,res)
        self.dfs(board,node,i,j-1,path+temp,res)
        board[i][j] = temp

Design

359 Logger Rate Limiter
346 Moving Average from Data Stream (Sliding Window)
362 Design Hit Counter
281 Zigzag Iterator
284 Peeking Iterator
251 Flatten 2D Vector
288 Unique Word Abbreviation
170 Two Sum III - Data structure design
348 Design Tic-Tac-Toe
379 Design Phone Directory
353 Design Snake Game

146. LRU Cache(Hard)

設計並實現最近最少使用(LRU)緩存的數據結構。它應該支持以下操作:get和put。
在這裏插入圖片描述

get(key) - Get the value (will always be positive) of the key if the key exists in the cache, otherwise return -1.
put(key, value) - Set or insert the value if the key is not already present. When the cache reached its capacity, it should invalidate the least recently used item before inserting a new item.

思路:
由於要求時間複雜度爲O(1),定義一個雙向鏈表(自帶一個頭結點和尾結點)
1、init() 包括capacity、字典、頭尾結點
2、get()判斷是否在字典、然後移除該結點,再加入該結點(保證在末尾)
3、set()判斷是否包含關鍵詞,如果有,刪除並更改。如果容量不夠,刪掉最後一個。字典也要記得刪
4、定義node結點和remove函數和add函數

class Node(object):
    def __init__(self,key,value):
        self.key = key
        self.value = value
        self.next = None
        self.pre = None

class LRUCache(object):

    def __init__(self, capacity):
        """
        :type capacity: int
        """
        self.capacity = capacity
        self.dict = {}
        self.head = Node(0,0)
        self.tail = Node(0,0)
        self.head.next = self.tail
        self.tail.pre = self.head

    def get(self, key):
        """
        :type key: int
        :rtype: int
        """
        if key in self.dict:
            self.remove(self.dict[key])
            self.add(self.dict[key])
            return self.dict[key].value
        return -1
        

    def put(self, key, value):
        """
        :type key: int
        :type value: int
        :rtype: None
        """
        if key in self.dict:
            self.remove(self.dict[key])
        n = Node(key,value)
        self.dict[key] = n
        self.add(n)
        if len(self.dict)>self.capacity:
            r = self.head.next
            self.remove(r)
            del self.dict[r.key]
    def remove(self,node):
        next = node.next
        next.pre = node.pre
        node.pre.next = next
    
    def add(self,node):
        pre = self.tail.pre
        pre.next = node
        node.next = self.tail
        node.pre = pre
        self.tail.pre = node

355 Design Twitter
303 Range Sum Query - Immutable
304 Range Sum Query 2D - Immutable
307 Range Sum Query - Mutable Binary Index Tree
308 Range Sum Query 2D - Mutable Binary Index Tree

面經面試題

抖音紅人(字節跳動)

在這裏插入圖片描述
在這裏插入圖片描述

思路:
1、首先將第一層關係使用set字典形式存儲
2、間接關係使用三層循環,第一層循環人數,第二層循環字典,第三層循環字典內的元素
3、需要元素量等於總人數的,加一

import collections
class solution:
    def split_star(list,nums):
        if len(list) ==0:
            return 0
        res = collections.defaultdict(set)
        for i,v in enumerate(list):
            # 奇數表示拿到的是關注的對象 奇數前面的偶數表示關注此對象的人
            if i%2:
                res[v].add(v)
                res[v].add(list[i-1])
        # 需要多次檢測,因爲有間接的關注關係
        for i in range(nums):
            # star 表示被關注的人, user表示關注者
            for star,user in res.items():
                add_people = set()
                for u in user:
                    if u in res:
                        add_people |= res[u]
                res[star] |= add_people
        star_num = 0
        for star,user in res.items():
            if len(user) == nums:
                star_num +=1
        return star_num

if __name__ =='__main__':
    N = int(input())
    list = list(map(int,input().split()))
    print(solution.split_star(list,N))



695. Max Area of Island(Medium)(字節跳動)

給定0和1的非空二維陣列網格,島是一組1(表示陸地)4方向連接(水平或垂直)。您可以假設網格的所有四個邊緣都被水包圍。 找到給定2D陣列中島的最大面積。 (如果沒有島,則最大面積爲0.)
在這裏插入圖片描述

思路:
這個最大區域(不是面積)是指連接1的最大範圍,有就加一。
使用dfs進行遍歷
首先循環整個表格,然後如果該區域是1,則使用dfs進行遍歷,遍歷之前需要把當前節點設爲2,防止再次訪問

    def maxAreaOfIsland(self, grid):
        """
        :type grid: List[List[int]]
        :rtype: int
        """
        def dfs(i,j):
            if 0<=i<len(grid) and 0<=j<len(grid[0]) and grid[i][j] == 1:
                grid[i][j] = 'X'
                ans = 1
                for i in map(dfs,(i+1,i-1,i,i),(j,j,j+1,j-1)):
                    ans += i
                return ans
            return 0
        res = 0
        for i in range(len(grid)):
            for j in range(len(grid[0])):
                if grid[i][j] == 1:
                    res = max(res,dfs(i,j))
        return res

十大排序

在這裏插入圖片描述
In-place:佔用常數內存,不佔用額外內存
Out-place:佔用額外內存
n:數據規模
k:“桶”的個數
穩定性:排序後2個相等鍵值的順序和排序之前它們的順序相同

冒泡排序

冒泡排序每次找出一個最大的元素,因此需要遍歷 n-1 次。
在這裏插入圖片描述

def bubbleSort(nums):
    for i in range(len(nums) - 1): # 遍歷 len(nums)-1 次
        for j in range(len(nums) - i - 1): # 已排好序的部分不用再次遍歷
            if nums[j] > nums[j+1]:
                nums[j], nums[j+1] = nums[j+1], nums[j] # Python 交換兩個數不用中間變量
    return nums

選擇排序

選擇排序不受輸入數據的影響,即在任何情況下時間複雜度不變。選擇排序每次選出最小的元素,因此需要遍歷 n-1 次。
在這裏插入圖片描述

def selectionSort(nums):
    for i in range(len(nums) - 1):  # 遍歷 len(nums)-1 次
        minIndex = i
        for j in range(i + 1, len(nums)):
            if nums[j] < nums[minIndex]:  # 更新最小值索引
                minIndex = j  
        nums[i], nums[minIndex] = nums[minIndex], nums[i] # 把最小數交換到前面
    return nums

插入排序

插入排序如同打撲克一樣,每次將後面的牌插到前面已經排好序的牌中。插入排序有一種優化算法,叫做拆半插入。因爲前面是局部排好的序列,因此可以用折半查找的方法將牌插入到正確的位置,而不是從後往前一一比對。折半查找只是減少了比較次數,但是元素的移動次數不變,所以時間複雜度仍爲 O(n^2) !
在這裏插入圖片描述

def insertionSort(nums):
    for i in range(len(nums) - 1):  # 遍歷 len(nums)-1 次
        curNum, preIndex = nums[i+1], i  # curNum 保存當前待插入的數
        while preIndex >= 0 and curNum < nums[preIndex]: # 將比 curNum 大的元素向後移動
            nums[preIndex + 1] = nums[preIndex]
            preIndex -= 1
        nums[preIndex + 1] = curNum  # 待插入的數的正確位置   
    return nums

希爾排序

希爾排序是插入排序的一種更高效率的實現。它與插入排序的不同之處在於,它會優先比較距離較遠的元素。
【例子】對於待排序列 {44,12,59,36,62,43,94,7,35,52,85},我們可設定增量序列爲 {5,3,1}。
【解析】第一個增量爲 5,因此 {44,43,85}、{12,94}、{59,7}、{36,35}、{62,52} 分別隸屬於同一個子序列,子序列內部進行插入排序;然後選取第二個增量3,因此 {43,35,94,62}、{12,52,59,85}、{7,44,36} 分別隸屬於同一個子序列;最後一個增量爲 1,這一次排序相當於簡單插入排序,但是經過前兩次排序,序列已經基本有序,因此此次排序時間效率就提高了很多。希爾排序過程如下:
在這裏插入圖片描述

def shellSort(nums):
    lens = len(nums)
    gap = 1  
    while gap < lens // 3:
        gap = gap * 3 + 1  # 動態定義間隔序列
    while gap > 0:
        for i in range(gap, lens):
            curNum, preIndex = nums[i], i - gap  # curNum 保存當前待插入的數
            while preIndex >= 0 and curNum < nums[preIndex]:
                nums[preIndex + gap] = nums[preIndex] # 將比 curNum 大的元素向後移動
                preIndex -= gap
            nums[preIndex + gap] = curNum  # 待插入的數的正確位置
        gap //= 3  # 下一個動態間隔
    return nums
					

歸併排序

歸併排序須知:
作爲一種典型的分而治之思想的算法應用,歸併排序的實現由兩種方法:

  1. 自上而下的遞歸(所有遞歸的方法都可以用迭代重寫,所以就有了第2種方法)
  2. 自下而上的迭代

和選擇排序一樣,歸併排序的性能不受輸入數據的影響,但表現比選擇排序好的多,因爲始終都是O(n log n)的時間複雜度。代價是需要額外的內存空間。
在這裏插入圖片描述
遞歸

def mergeSort(nums):
    # 歸併過程
    def merge(left, right):
        result = []  # 保存歸併後的結果
        i = j = 0
        while i < len(left) and j < len(right):
            if left[i] <= right[j]:
                result.append(left[i])
                i += 1
            else:
                result.append(right[j])
                j += 1
        result = result + left[i:] + right[j:] # 剩餘的元素直接添加到末尾
        return result
    # 遞歸過程
    if len(nums) <= 1:
        return nums
    mid = len(nums) // 2
    left = mergeSort(nums[:mid])
    right = mergeSort(nums[mid:])
    return merge(left, right)

非遞歸

#low = low +2*i   #middle = low +i  #high = min(low+2*i,len(seq))
def merge_sort(seq):
    i = 1 # i是步長
    while i < len(seq):
        low = 0
        while low < len(seq):
            mid = low + i #mid前後均爲有序
            high = min(low+2*i,len(seq))
            if mid < high: 
                merge(seq, low, mid, high)
            low += 2*i
        i *= 2
def merge(seq, low, mid, high):
比上一步多了一個mid,其它一樣

快速排序

又是一種分而治之思想在排序算法上的典型應用。本質上來看,快速排序應該算是在冒泡排序基礎上的遞歸分治法。它是處理大數據最快的排序算法之一,雖然 Worst Case 的時間複雜度達到了 O(n²),但是在大多數情況下都比平均時間複雜度爲 O(n log n) 的排序算法表現要更好,因爲 O(n log n) 記號中隱含的常數因子很小,而且快速排序的內循環比大多數排序算法都要短小,這意味着它無論是在理論上還是在實際中都要更快,比複雜度穩定等於 O(n log n) 的歸併排序要小很多。所以,對絕大多數順序性較弱的隨機數列而言,快速排序總是優於歸併排序。它的主要缺點是非常脆弱,在實現時要非常小心才能避免低劣的性能。
在這裏插入圖片描述

def quickSort(nums):  # 這種寫法的平均空間複雜度爲 O(nlogn)
    if len(nums) <= 1:
        return nums
    pivot = nums[0]  # 基準值
    left = [nums[i] for i in range(1, len(nums)) if nums[i] < pivot] 
    right = [nums[i] for i in range(1, len(nums)) if nums[i] >= pivot]
    return quickSort(left) + [pivot] + quickSort(right)

'''
@param nums: 待排序數組
@param left: 數組上界
@param right: 數組下界
'''
def quickSort2(nums, left, right):  # 這種寫法的平均空間複雜度爲 O(logn) 
    # 分區操作
    def partition(nums, left, right):
        pivot = nums[left]  # 基準值
        while left < right:
            while left < right and nums[right] >= pivot:
                right -= 1
            nums[left] = nums[right]  # 比基準小的交換到前面
            while left < right and nums[left] <= pivot:
                left += 1
            nums[right] = nums[left]  # 比基準大交換到後面
        nums[left] = pivot # 基準值的正確位置,也可以爲 nums[right] = pivot
        return left  # 返回基準值的索引,也可以爲 return right
    # 遞歸操作
    if left < right:
        pivotIndex = partition(nums, left, right)
        quickSort2(nums, left, pivotIndex - 1)  # 左序列
        quickSort2(nums, pivotIndex + 1, right) # 右序列
    return nums

堆排序

堆排序可以說是一種利用堆的概念來排序的選擇排序。分爲兩種方法:

  1. 大根堆:每個節點的值都大於或等於其子節點的值,用於升序排列;
  2. 小根堆:每個節點的值都小於或等於其子節點的值,用於降序排列。

如下圖所示,首先將一個無序的序列生成一個最大堆,如圖(a)所示。接下來我們不需要將堆頂元素輸出,只要將它與堆的最後一個元素對換位置即可,如圖(b)所示。這時我們確知最後一個元素 99 一定是遞增序列的最後一個元素,而且已經在正確的位置上。 現在問題變成了如何將剩餘的元素重新生成一個最大堆——也很簡單,只要依次自上而下進行過濾,使其符合最大堆的性質。圖(c)是調整後形成的新的最大堆。要注意的是,99 已經被排除在最大堆之外,即在調整的時候,堆中元素的個數應該減 1 。結束第 1 輪調整後,再次將當前堆中的最後一個元素 22 與堆頂元素換位,如圖(d)所示,再繼續調整成新的最大堆……如此循環,直到堆中只剩 1 個元素,即可停止,得到一個從小到大排列的有序序列。
在這裏插入圖片描述

# 大根堆(從小打大排列)
def heapSort(nums):
    # 調整堆
    def adjustHeap(nums, i, size):
        # 非葉子結點的左右兩個孩子
        lchild = 2 * i + 1
        rchild = 2 * i + 2
        # 在當前結點、左孩子、右孩子中找到最大元素的索引
        largest = i 
        if lchild < size and nums[lchild] > nums[largest]: 
            largest = lchild 
        if rchild < size and nums[rchild] > nums[largest]: 
            largest = rchild 
        # 如果最大元素的索引不是當前結點,把大的結點交換到上面,繼續調整堆
        if largest != i: 
            nums[largest], nums[i] = nums[i], nums[largest] 
            # 第 2 個參數傳入 largest 的索引是交換前大數字對應的索引
            # 交換後該索引對應的是小數字,應該把該小數字向下調整
            adjustHeap(nums, largest, size)
    # 建立堆
    def builtHeap(nums, size):
        for i in range(len(nums)//2)[::-1]: # 從倒數第一個非葉子結點開始建立大根堆
            adjustHeap(nums, i, size) # 對所有非葉子結點進行堆的調整
        # print(nums)  # 第一次建立好的大根堆
    # 堆排序 
    size = len(nums)
    builtHeap(nums, size) 
    for i in range(len(nums))[::-1]: 
        # 每次根結點都是最大的數,最大數放到後面
        nums[0], nums[i] = nums[i], nums[0] 
        # 交換完後還需要繼續調整堆,只需調整根節點,此時數組的 size 不包括已經排序好的數
        adjustHeap(nums, 0, i) 
    return nums  # 由於每次大的都會放到後面,因此最後的 nums 是從小到大排列

計數排序

計數排序要求輸入數據的範圍在 [0,N-1] 之間,則可以開闢一個大小爲 N 的數組空間,將輸入的數據值轉化爲鍵存儲在該數組空間中,數組中的元素爲該元素出現的個數。它是一種線性時間複雜度的排序。
在這裏插入圖片描述

def countingSort(nums):
    bucket = [0] * (max(nums) + 1) # 桶的個數
    for num in nums:  # 將元素值作爲鍵值存儲在桶中,記錄其出現的次數
        bucket[num] += 1
    i = 0  # nums 的索引
    for j in range(len(bucket)):
        while bucket[j] > 0:
            nums[i] = j
            bucket[j] -= 1
            i += 1
    return nums

桶排序

桶排序須知:
桶排序是計數排序的升級版。它利用了函數的映射關係,高效與否的關鍵就在於這個映射函數的確定。
爲了使桶排序更加高效,我們需要做到這兩點:

在額外空間充足的情況下,儘量增大桶的數量
使用的映射函數能夠將輸入的 N 個數據均勻的分配到 K 個桶中

同時,對於桶中元素的排序,選擇何種比較排序算法對於性能的影響至關重要。
什麼時候最快(Best Cases):
當輸入的數據可以均勻的分配到每一個桶中
什麼時候最慢(Worst Cases):
當輸入的數據被分配到了同一個桶中

def bucketSort(nums, defaultBucketSize = 5):
    maxVal, minVal = max(nums), min(nums)
    bucketSize = defaultBucketSize  # 如果沒有指定桶的大小,則默認爲5
    bucketCount = (maxVal - minVal) // bucketSize + 1  # 數據分爲 bucketCount 組
    buckets = []  # 二維桶
    for i in range(bucketCount):
        buckets.append([])
    # 利用函數映射將各個數據放入對應的桶中
    for num in nums:
        buckets[(num - minVal) // bucketSize].append(num)
    nums.clear()  # 清空 nums
    # 對每一個二維桶中的元素進行排序
    for bucket in buckets:
        insertionSort(bucket)  # 假設使用插入排序
        nums.extend(bucket)    # 將排序好的桶依次放入到 nums 中
    return nums

基數排序

基數排序是桶排序的一種推廣,它所考慮的待排記錄包含不止一個關鍵字。例如對一副牌的整理,可將每張牌看作一個記錄,包含兩個關鍵字:花色、面值。一般我們可以將一個有序列是先按花色劃分爲四大塊,每一塊中又再按面值大小排序。這時“花色”就是一張牌的“最主位關鍵字”,而“面值”是“最次位關鍵字”。
基數排序有兩種方法:

MSD (主位優先法):從高位開始進行排序
LSD (次位優先法):從低位開始進行排序
在這裏插入圖片描述

# LSD Radix Sort
def radixSort(nums):
    mod = 10
    div = 1
    mostBit = len(str(max(nums)))  # 最大數的位數決定了外循環多少次
    buckets = [[] for row in range(mod)] # 構造 mod 個空桶
    while mostBit:
        for num in nums:  # 將數據放入對應的桶中
            buckets[num // div % mod].append(num)
        i = 0  # nums 的索引
        for bucket in buckets:  # 將數據收集起來
            while bucket:
                nums[i] = bucket.pop(0) # 依次取出
                i += 1
        div *= 10
        mostBit -= 1
    return nums

基數排序 vs 計數排序 vs 桶排序
這三種排序算法都利用了桶的概念,但對桶的使用方法上有明顯差異:
基數排序:根據鍵值的每位數字來分配桶
計數排序:每個桶只存儲單一鍵值
桶排序:每個桶存儲一定範圍的數值
哪些排序算法可以在未結束排序時找出第 k 大元素?
冒泡、選擇、堆排序、快排(想想爲什麼?)

快排、歸併排序、堆排序、計數排序(桶排序)一般是面試中常問的題目,其中比較難的是堆排序,因爲涉及建堆、調整堆的過程,手寫該算法還是有一定難度的。

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