3月快刷了一半的题想起来应该记录一下…前几天有用JAVA写的有用C++写的,但后面没有标注的都是用Python写的。
小白一枚,有不对的或者需要改进的地方恳请各位大佬批评指正!
常规操作,模版题
1.Python版本
class MyStack:
def __init__(self):
"""
Initialize your data structure here.
"""
self.queue=collections.deque()
def push(self, x: int) -> None:
"""
Push element x onto stack.
"""
self.queue.append(x)
length=len(self.queue)
while length>1:
self.queue.append(self.queue.popleft())
length-=1
def pop(self) -> int:
"""
Removes the element on top of the stack and returns that element.
"""
return self.queue.popleft()
def top(self) -> int:
"""
Get the top element.
"""
return self.queue[0]
def empty(self) -> bool:
"""
Returns whether the stack is empty.
"""
return not bool(self.queue)
# Your MyStack object will be instantiated and called as such:
# obj = MyStack()
# obj.push(x)
# param_2 = obj.pop()
# param_3 = obj.top()
# param_4 = obj.empty()
C++版本
class MyStack {
private:
queue<int> q;
public:
/** Initialize your data structure here. */
MyStack() {
}
/** Push element x onto stack. */
void push(int x) {
int size=q.size();
q.push(x);
while(size--){
int temp=q.front();
q.pop();
q.push(temp);
}
}
/** Removes the element on top of the stack and returns that element. */
int pop() {
int topEle=q.front();
q.pop();
return topEle;
}
/** Get the top element. */
int top() {
return q.front();
}
/** Returns whether the stack is empty. */
bool empty() {
return q.empty();
}
};
Java版本
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public ListNode reverseList(ListNode head) {
if (head == null || head.next == null) return head;
ListNode p = reverseList(head.next);
head.next.next = head;
head.next = null;
return p;
}
}
从后往前进行,比较AB两个数组,那个大放进去哪个。
最后要考虑,如果最后只剩下B,则把B完全放到A的前面。
Python版本
class Solution:
def merge(self, A: List[int], m: int, B: List[int], n: int) -> None:
"""
Do not return anything, modify A in-place instead.
"""
size=len(A)-1
n-=1
m-=1
while(size and n>-1 and m>-1):
if A[m]>=B[n]:
A[size]=A[m]
m-=1
else:
A[size]=B[n]
n-=1
size-=1
if n>-1:
for j in range(0,n+1):
A[j]=B[j]
j+=1
这是一道BFS模版题。
Python版本:
广度优先搜索算法
class Solution:
def orangesRotting(self, grid: List[List[int]]) -> int:
row=len(grid)
line=len(grid[0])
time=0
direction=[(0,1),(1,0),(0,-1),(-1,0)]
queue=collections.deque()
fresh=0
for i in range(row):
for j in range(line):
if grid[i][j]==2:
queue.append((i,j,0))
elif grid[i][j]==1:
fresh+=1
while queue:
i,j,time=queue.popleft()
for di,dj in direction:
if 0<=i+di<row and 0<=j+dj<line and grid[i+di][j+dj]==1:
grid[i+di][j+dj]=2
fresh-=1
queue.append((i+di,j+dj,time+1))
if fresh>0:
return -1
else:
return time
C++版本:
//C++
class Solution {
public int orangesRotting(int[][] grid) {
int[] dr=new int[]{-1,0,1,0};
int[] dc=new int[]{0,-1,0,1};
int R=grid.length; //行数
int C=grid[0].length; //列数
int code=0;
Queue<Integer> queue=new ArrayDeque();
Map<Integer,Integer> depth=new HashMap();
for(int r=0;r<R;++r){
for(int c=0;c<C;++c){
if(grid[r][c]==2){//找出腐烂的橙子
code=r*C+c;//获得橙子在数组中的位置
queue.add(code);//放入队列
depth.put(code,0);//将此时所有腐烂的橙子的时间节点记为0
}
}
}
int alltime=0;
while(!queue.isEmpty()){ //开始进行BFS
code=queue.remove();
int r=code/C;
int c=code%C;
for(int k=0;k<4;++k){
int nr=r+dr[k];
int nc=c+dc[k];
if(0<=nr&&nr<R&&0<=nc&&nc<C&&grid[nr][nc]==1){ //对左下右上几个方向进行判断
grid[nr][nc]=2;
int ncode=nr*C+nc;
queue.add(ncode); //将新变成的腐烂的橘子入队列
depth.put(ncode,depth.get(code)+1);
alltime=depth.get(ncode);
}
}
}
for(int[]row:grid)
for(int v:row)
if(v==1)
return -1;
return alltime;
}
}
学会的知识点:
1.Queue queue=new ArrayDeque();——ArrayDeque
队列的:
栈:
(参考:https://www.cnblogs.com/loveLands/articles/9708717.html)
2.Map
其实就是一个键值对接口。
函数原型为:Map
在这个声明中,map就是一个容器,主要调用put()、get()方法;
例如:map.put(“user”, obj);
obj是之前声明的一个类的对象,比如为user1,那么就是把user这个对象放到了map容器中了,其中user只是对象的代号,在调用的时候取出,代码为map.get(user);
参数是前面写的代号,得到的是user1这个对象。
HashMap就是一个散列表,它是通过“拉链法”解决哈希冲突。
(参考:https://blog.csdn.net/Dreamlzd/article/details/77827058)
class Solution {
public:
vector<int> distributeCandies(int candies, int num_people) {
vector<int> ans(num_people,0);
int i = 0;
while (candies != 0) {
ans[i % num_people] += min(candies, i + 1);
candies -= min(candies, i + 1);
++i;
}
return ans;
}
};
学习点:
对于一个需要循环操作的数组:
ans[i % num_people] += min(candies, i + 1); //数组中i % num_people这个写的方法很巧妙。
C++版本
class Solution {
public:
vector<vector<int>> findContinuousSequence(int target) {
vector<vector<int>> result;
vector<int> line;
int sum=0;
int count=(target-1)/2; //等效于target/2取下整
int i,j;
for(i=1;i<=count;i++){
for(j=i;;j++){
sum+=j;
if(sum==target){
line.clear();
for(int k=i;k<=j;k++)
line.emplace_back(k); //存放和为目标值的这几个数
result.emplace_back(line); //存放这个数组
sum=0;
break;
}
if(sum>target){
sum=0;
break;
}
}
}
return result;
}
};
class MaxQueue {
private:
queue<int> q; //用来记录插入的值
deque<int> max;//用来维护最大值
public:
MaxQueue() {
}
int max_value() {
if(max.empty()) return -1;
return max.front();
}
void push_back(int value) {
while(!max.empty()&&value>max.back()){
max.pop_back();
}
max.push_back(value);
q.push(value);
}
int pop_front() {
if(q.empty()) return -1;
int num=q.front();
if(num==max.front())
max.pop_front();
q.pop();
return num;
}
};
/**
* Your MaxQueue object will be instantiated and called as such:
* MaxQueue* obj = new MaxQueue();
* int param_1 = obj->max_value();
* obj->push_back(value);
* int param_3 = obj->pop_front();
*/
大佬总结,模版,常看常背默https://leetcode-cn.com/problems/coin-change/solution/dong-tai-gui-hua-tao-lu-xiang-jie-by-wei-lai-bu-ke/
常规的动态解题方法:
class Solution {
vector<int> count;
int dp(vector<int>& coins,int amount){
if(amount<0) return -1;
if(amount==0) return 0;
if(count[amount-1]!=0) return count[amount-1];
int Min=INT_MAX;
for(int coin:coins){
int res=dp(coins,amount-coin);
if(res>=0&&res<Min)
Min=res+1;
}
count[amount-1]=Min==INT_MAX?-1:Min;
return count[amount-1];
}
public:
int coinChange(vector<int>& coins, int amount) {
if(amount<1) return 0;
count.resize(amount);//设置count大小
return dp(coins,amount);
}
};
方法二:贪心+dfs思路:
void coinChange(vector<int>& coins, int amount, int c_index, int count, int& ans)
{
if (amount == 0)
{
ans = min(ans, count);
return;
}
if (c_index == coins.size()) return;
for (int k = amount / coins[c_index]; k >= 0 && k + count < ans; k--)
{
coinChange(coins, amount - k * coins[c_index], c_index + 1, count + k, ans);
}
}
int coinChange(vector<int>& coins, int amount)
{
if (amount == 0) return 0;
sort(coins.rbegin(), coins.rend()); //反向迭代器,从大到小排序
int ans = INT_MAX;
coinChange(coins, amount, 0, 0, ans);
return ans == INT_MAX ? -1 : ans;
}
C++解法
class Solution {
public:
int maxProfit(vector<int>& prices) {
/* int last = 0, profit = 0;
for (int i = 0; i < (int)prices.size() - 1; ++i) {
last = max(0, last + prices[i+1] - prices[i]);
profit = max(profit, last);
}
return profit;*/
int dp_i_0=0,dp_i_1=INT_MIN;
for(int i=0;i<prices.size();i++){
dp_i_0=max(dp_i_0,dp_i_1+prices[i]);
dp_i_1=max(dp_i_1,-prices[i]);
}
return dp_i_0;
}
};
这是一连串的dp模版题,其他同样的买股票问题我单写了一篇:
https://blog.csdn.net/chuxuezheerer/article/details/104848202
其实就是求左右子树的最大深度。
C++:
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
int res=1;
int depth(TreeNode* root){
if(root==NULL) return 0;
int left=depth(root->left);
int right=depth(root->right);
res=max(res,left+right+1);
return max(left,right)+1;
}
public:
int diameterOfBinaryTree(TreeNode* root) {
depth(root);
return res-1;
}
};
class Solution {
public:
bool canThreePartsEqualSum(vector<int>& A) {
int sum=accumulate(A.begin(),A.end(),0);
if(sum%3!=0) return false;//首先要求数组长度必须是3的倍数才能分成三堆
int target=sum/3;
int i=0;
int length=A.size();
int res=0;
while(i<length){
res=res+A[i];
if(res==target)
break;
i++;
}
if (res!=target) return false;
int j=i+1;
while(j<length-1){
res+=A[j];
if(res==2*target)
return true;
j++;
}
return false;
}
};
C++
class Solution {
bool check(string t,string str){
int lens=(int)str.length()/(int)t.length();
string res="";
for(int i=1;i<=lens;i++)
res+=t;
return res==str;
}
public:
string gcdOfStrings(string str1, string str2) {
int len1=(int)str1.length();
int len2=(int)str2.length();
string T=str1.substr(0,__gcd(len1,len2));
if(check(T,str1)&&check(T,str2))
return T;
return "";
}
};
这道题最简单的是一种数学方法叫摩尔投票法。
方法二是常规的哈希方法。
class Solution:
def majorityElement(self, nums: List[int]) -> int:
s=len(nums)
count=0
for i in range(s):
if count==0:
target=nums[i]
if nums[i]==target:
count+=1
else:
count-=1
return target
# 方法二:
# count=len(nums)//2
# dict={}
# for num in nums:
# if num in dict:
# dict[num]+=1
# else:
# dict[num]=1
# if dict[num]>count:
# return num;
class Solution:
def lengthOfLIS(self, nums: List[int]) -> int:
dp=[0]*len(nums) #从小到大存放nums数组
res=0
for num in nums:
i,j=0,res
while i<j:
mid=(i+j)//2 #其实相当于在dp数组查找中运用二分法
if dp[mid]<num :
i=mid+1
else:
j=mid
dp[i]=num
if j==res:res+=1
return res
方法二,相当于存放每个数组的逆序数
class Solution:
def lengthOfLIS(self, nums: List[int]) -> int:
if not nums: return 0
dp = [1] * len(nums)
for i in range(len(nums)):
for j in range(i):
if nums[j] < nums[i]:
dp[i] = max(dp[i], dp[j] + 1)
return max(dp)
方法一:深度优先遍历BFS
#Python3
class Solution:
def maxAreaOfIsland(self, grid: List[List[int]]) -> int:
m=len(grid)
if m==0:return 0
n=len(grid[0])
def dfs(i,j):
if i<0 or i>=m or j<0 or j>=n:
return 0;
if grid[i][j]==0:
return 0
grid[i][j]=0
top=dfs(i,j-1)
bottom=dfs(i,j+1)
left=dfs(i-1,j)
right=dfs(i+1,j)
res=1+sum([top,bottom,left,right])
return res
ans=0
for i in range(m):
for j in range(n):
ans=max(ans,dfs(i,j))
return ans
方法二:用广度优先遍历
#Python
class Solution:
def maxAreaOfIsland(self, grid: List[List[int]]) -> int:
row=len(grid)
line=len(grid[0])
#官网答案写法:
#for i, l in enumerate(grid):
#enumerate()函数:讲一个可遍历的数据对象组合成一个索引序列,同时列出数据和数据下标。
#for j, n in enumerate(l):
res=0
for i in range(row):
for j in range(line):
queue=collections.deque([(i,j)])
cur=0
while queue:
cur_i,cur_j=queue.popleft()
if cur_i<0 or cur_j<0 or cur_i>=row or cur_j>=line or grid[cur_i][cur_j]!=1:
continue
cur+=1
grid[cur_i][cur_j]=0
for di,dj in [[1,0],[0,1],[0,-1],[-1,0]]:
next_i=cur_i+di
next_j=cur_j+dj
queue.append((next_i,next_j))
res=max(res,cur)
return res
注意是求连通的边界值!
class Solution:
def colorBorder(self, grid: List[List[int]], r0: int, c0: int, color: int) -> List[List[int]]:
if not grid or len(grid[0])==0:
return grid
row=len(grid)
line=len(grid[0])
queue=collections.deque([(r0,c0)])
visit={(r0,c0)} #记录被访问的位置
res=grid[r0][c0] #记录最开始的颜色
while queue:
i,j=queue.popleft()
for di,dj in [[0,1],[-1,0],[0,-1],[1,0]]:
cur_i=i+di
cur_j=j+dj
if (cur_i,cur_j) in visit:
continue #如果已经被访问过,则跳过后面步骤
if cur_i<0 or cur_j<0 or cur_i>=row or cur_j>=line or grid[cur_i][cur_j]!=res:
grid[i][j]=color #边界判断,如果为边界值,则原i,j位置的color变
elif 0<=cur_i<row and 0<=cur_j<line and grid[cur_i][cur_j]==res:
queue.append((cur_i,cur_j))
visit.add((cur_i,cur_j)) #将连通的值入栈,入访问序列
return grid
class Solution:
def compressString(self, S: str) -> str:
if not S:
return S
ch=S[0]
res=''
count=0
for num in S:
if num==ch:
count+=1
else:
res+=ch+str(count)
count=1
ch=num
res+=ch+str(count)
return res if len(res)<len(S) else S
在Python中用一个字典统计,遍历每一个单词,如果这个单词每个字母对应的数量小于整个字典统计每个字母的数量,则可以构成这个单词。
class Solution:
def countCharacters(self, words: List[str], chars: str) -> int:
char_count=collections.Counter(chars)
res=0
for word in words:
word_count=collections.Counter(word)
if all(word_count[i]<=char_count[i] for i in word_count):
res+=len(word)
return res
class Solution:
def isRectangleOverlap(self, rec1: List[int], rec2: List[int]) -> bool:
x_left=max(rec1[0],rec2[0])
x_right=min(rec1[2],rec2[2])
y_top=min(rec1[3],rec2[3])
y_bottom=max(rec1[1],rec2[1])
#右边最小的>左边最大的,则横坐标交叉;
#上边最小的>下边最大的,则纵坐标交叉
if x_right-x_left>0 and y_top-y_bottom>0:
return True
else:
return False
还是要考虑数学特性,所有数量为偶数的可以直接要,数量为奇数的,可以-1变成偶数加入,最后结果如果原来含有奇数要在+1,因为可以以中心为原点形成对称。
class Solution:
def longestPalindrome(self, s: str) -> int:
length=len(s)
num=collections.Counter(s)
res=0
max_odd=0
for key in num:
if(num[key]%2==0):
res+=num[key]
else:
res+=num[key]-1
return res if length==res else res+1
如果不考虑用Python原函数直接排序(如果面试中用的话会被打死吧~~~),就用堆排序或者快排
快排思想排序:
class Solution:
def getLeastNumbers(self, arr: List[int], k: int) -> List[int]:
length=len(arr)
if k<=0 or k>length:
return list()
left,right=0,length-1
while left<right:
res=self.partition(arr,left,right)
if res==k-1:
return
elif res<k-1:
res=self.partition(arr,res+1,right)
else:
res=self.partition(arr,left,res-1)
return arr[:k]
def partition(self,arr,left,right):
cur_res=arr[left]
while left<right:
while left<right and arr[right]>=cur_res:
right-=1
arr[left]=arr[right]
while left<right and arr[left]<=cur_res:
left+=1
arr[right]=arr[left]
arr[left]=cur_res
return left
堆排序,这个要注意,求前K小,用的是大根堆排序。先对前K个数字进行排序,然后再从K+1个数开始,如果小于堆顶,则把堆顶推出(说明堆顶肯定不是前K小的数字),将这个数放到堆顶,在进行调整。
class Solution:
def getLeastNumbers(self, arr: List[int], k: int) -> List[int]:
length=len(arr)
begin=k//2-1
while begin>=0:
self.adjust_heap(arr,k,begin)
begin-=1
for i in range(k,length):
if arr[i]<arr[0]:#比较这个元素和堆顶元素,若小,
arr[0]=arr[i]#则代替堆顶
self.adjust_heap(arr,k,0)#进行大根堆排序
return arr[:k]#最后返回前K个数即为所求,但不一定有顺序
def adjust_heap(self,arr,k,begin):
num=begin
child=num*2+1
while num<k and child<k:
if child+1<k and arr[child]<arr[child+1]:
child=child+1
if arr[child]>arr[num]:
arr[num],arr[child]=arr[child],arr[num]
num=child
child=child*2+1
方法一:水壶倒水一共有几种状态,将这些状态全部遍历,然后用一个集合记录经历的状态(防止反复经历,造成无尽的循环)。
class Solution:
def canMeasureWater(self, x: int, y: int, z: int) -> bool:
stack = [(0, 0)]
self.seen = set()
while stack:
remain_x, remain_y = stack.pop()
if remain_x == z or remain_y == z or remain_x + remain_y == z:
return True
if (remain_x, remain_y) in self.seen:
continue
self.seen.add((remain_x, remain_y))
# 把 X 壶灌满。
stack.append((x, remain_y))
# 把 Y 壶灌满。
stack.append((remain_x, y))
# 把 X 壶倒空。
stack.append((0, remain_y))
# 把 Y 壶倒空。
stack.append((remain_x, 0))
# 把 X 壶的水灌进 Y 壶,直至灌满或倒空。
stack.append((remain_x - min(remain_x, y - remain_y), remain_y + min(remain_x, y - remain_y)))
# 把 Y 壶的水灌进 X 壶,直至灌满或倒空。
stack.append((remain_x + min(remain_y, x - remain_x), remain_y - min(remain_y, x - remain_x)))
return False
方法二:数学方法,这个要积累了,应用裴蜀定理
裴蜀定理(或贝祖定理):对任何整数a、b和它们的最大公约数d,关于未知数x和y的线性不定方程(称为裴蜀等式):若a,b是整数,且gcd(a,b)=d,那么对于任意的整数x,y,ax+by都一定是d的倍数,特别地,一定存在整数x,y,使ax+by=d成立。
class Solution:
def canMeasureWater(self, x: int, y: int, z: int) -> bool:
if x + y < z:
return False
if x == 0 or y == 0:
return z == 0 or x + y == z
return z % math.gcd(x, y) == 0
先把原来的list排序,让每后一个元素均大于前一个元素。每个元素值各不相同,增加最少的次数即位所有。有点贪心算法的意思。
class Solution:
def minIncrementForUnique(self, A: List[int]) -> int:
A.sort()
count=0
for i in range(1,len(A)):
if A[i-1]>=A[i]:
count+=A[i-1]-A[i]+1
A[i]=A[i-1]+1
return count
自己的解题思路:设计一个right指针,首先遍历一边链表,获得链表长度,计算中心点的位置,然后再移动head指针到最终位置上
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def middleNode(self, head: ListNode) -> ListNode:
if head==None:
return
right=head
res=1
while right.next!=None:
right=right.next
res+=1
for i in range(res//2):
head=head.next
return head
看官方解题答案后,觉得使用快慢指针这个思想很好,使用slow,fast两个指针,fast移动速度是slow的两倍,那么等到fast移动到末尾的时候,slow正好在中间。
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def middleNode(self, head: ListNode) -> ListNode:
slow=fast=head
while fast and fast.next:
slow=slow.next
fast=fast.next.next
return slow
典型的动态规划问题。
分状态回答,设两个状态,
dp[i][0]=max(dp[i-1][0],dp[i-1][1]) #此时没有按摩状态=max(昨天没有按摩状态,昨天有按摩状态)
dp[i][1]=dp[i-1][0]+nums[i] #今天有按摩状态=昨天没有按摩状态+今天按摩了
class Solution:
def massage(self, nums: List[int]) -> int:
length=len(nums)
if length==0:
return 0
dp_0=tdp_0=0
dp_1=nums[0]
for i in range(1,length):
tdp_0=max(dp_0,dp_1)
dp_1=dp_0+nums[i]
dp_0=tdp_0
return max(dp_0,dp_1)
首先一个正方体有6个面,在同一个格子上的,1.要减去摞起来的面,设一个格子内有n个正方体,则要减去2(n-1)个面。2.要减去和这个相邻格子内的立方体的重叠数。
class Solution:
def surfaceArea(self, grid: List[List[int]]) -> int:
row=len(grid)
line=len(grid[0])
res=0
for i in range(row):
for j in range(line):
if grid[i][j]==0:#如果这个格子没有立方体,则不做任何操作
continue
res+=6*grid[i][j]-2*(grid[i][j]-1) #在一个格子内叠起来的立方体
for di,dj in [[1,0],[0,-1],[-1,0],[0,1]]:#类似DFS遍历
if 0<=i+di<row and 0<=j+dj<line:
res-=min(grid[i][j],grid[i+di][j+dj])#减去两个相邻格子间最小数的面
return res
这个题有点深度优先遍历的算法思想,但这里没有用递归也没有用栈来辅助,反而用了四次循环的思想。
class Solution:
def numRookCaptures(self, board: List[List[str]]) -> int:
row=len(board)
line=len(board[0])
si,sj=0,0
dx,dy=[0,1,0,-1],[1,0,-1,0]
for i in range(row):
for j in range(line):
if board[i][j]=="R":
si,sj=i,j
break #先遍历整个数组,找到白色车所在的位置
res=0
for i in range(4):#四个方向进行
step=0
while True:
new_i=si+dx[i]*step
new_j=sj+dy[i]*step #新的位置
if new_i<0 or new_i>=8 or new_j<0 or new_j>=8 or board[new_i][new_j]=="B":#若碰到边界或者碰到白色的象
break
if board[new_i][new_j]=="p":#若碰到黑色的车
res+=1
break
step+=1 #如果什么都没碰到,那继续向前走
return res
先统计每个数字出现的次数,然后取各个出现次数的公约数,如果最大公约数大于2,则可以分组。
class Solution:
def hasGroupsSizeX(self, deck: List[int]) -> bool:
from fractions import gcd
count=collections.Counter(deck).values()
return reduce(gcd,count)>=2
这个题的内容对于我来说是一种崭新的内容。官方题解中用了两种方法:
方法一:存储后缀
1.discard()函数:discard() 方法用于移除指定的集合元素。 该方法不同于 remove() 方法,因为 remove() 方法在移除一个不存在的元素时会发生错误,而 discard() 方法不会。
class Solution:
def minimumLengthEncoding(self, words: List[str]) -> int:
good = set(words)
for word in words:
for k in range(1, len(word)):
good.discard(word[k:]) #例如good={time,me},那么操作过程是,移除"ime"(没有),移除"me"(有,移除me)
return sum(len(word) + 1 for word in good)
方法二:字典树
借鉴这位大佬的代码:https://leetcode-cn.com/problems/short-encoding-of-words/solution/python-zi-dian-shu-by-amir-6/
构造字典树是将字符串从后往前处理。最后的答案是叶子的层数+1.
这里借鉴两个大佬所写的
1.用递归
class Solution:
def minimumLengthEncoding(self, words: List[str]) -> int:
class Node:
def __init__(self, l):
self.l = l # 层数
self.children = {}
root = Node(0)
def build(t, w): # 递归构造字典树
if not w:
return
if w[-1] not in t.children:
t.children[w[-1]] = Node(t.l + 1)
build(t.children[w[-1]], w[:-1])
for w in words:
build(root, w)
ans = [0] # 相当于全局变量,以便在递归中累加
def vis(t): # 计算答案
if len(t.children) == 0: # 是叶子节点
if t.l > 0:
ans[0] += t.l + 1 # 累加
for c in t.children.values():
vis(c)
vis(root)
return ans[0]
class Trie:
def __init__(self): # 初始化
"""
Initialize your data structure here.
"""
self.Trie = {}
def insert(self, word): # 插入一个单词 word
"""
Inserts a word into the trie.
:type word: str
:rtype: void
"""
curr = self.Trie # 将字典赋给curr
for w in word: # 循环每个字母
if w not in curr: # 如果字母不在字典中
curr[w] = {} # 新建一个字典,用于存储这个w之后的字母
curr = curr[w] # 赋值给curr,这样下一个字母就会继续嵌套在字典的字典里
curr['#'] = 1 # 最后一个字母作为key,它的value本来是个{},现在使value='#'
def isTail(self, word): # 看word在不在trie中
"""
Returns if the word is in the trie.
:type word: str
:rtype: bool
"""
curr = self.Trie
for w in word:
curr = curr[w] # 随着字母的循环,一直走入嵌套的字典中
return len(curr) == 1 # 因为最后字母的value是#,
# 所以长度是1.如果Trie中没有这个词,那就没有最后的#
class Solution:
def minimumLengthEncoding(self, words: List[str]) -> int:
trie = Trie() # 初始化一个字典树
cnt = 0 # 记录长度
words = set(words) # 将words转为set,去掉重复的单词
for word in words:
trie.insert(word[::-1]) # 倒序插入字典树中,因为是要判断后缀
# 看trie.insert的函数构造,可以看出,如果这个单词是另一个单词的后缀,
# 那它是不会成为一条新的分支的,也就是说 判断它isTail是false的,因为要len==1
for word in words:
if trie.isTail(word[::-1]): # 看这个单词在不在字典树中
cnt += len(word) + 1
return cnt
参考链接:https://leetcode-cn.com/problems/short-encoding-of-words/solution/python3-hou-zhui-shu-mo-ban-ti-820-dan-ci-de-ya-su/
这道题还是DFSorBFS做法的变形
这里我用DFS来进行
class Solution:
def maxDistance(self, grid: List[List[int]]) -> int:
row=len(grid)
line=len(grid[0])
#dist用来存放距离陆地的距离
dist=[[float('inf') for _ in range(row)]for _ in range(line)]
#visit用来判断这个位置是否访问过
visit=[[False for _ in range(row)] for _ in range(line)]
queue=collections.deque()
land_num=0
res=0
total=row*line
#先将整个表格进行遍历,将陆地位置存入队列
for i in range(row):
for j in range(line):
if grid[i][j]==1:
dist[i][j]=0
visit[i][j]=True
queue.append((i,j))
land_num+=1
#记录陆地数,若全是陆地或者全是海洋,则返回-1
if land_num==total or land_num==0:
return -1
#然后开始广度优先遍历
while queue:
x,y=queue.popleft()
for i,j in [(x + 1, y), (x - 1, y), (x, y + 1), (x, y - 1)]:
if 0<=i<row and 0<=j<line and visit[i][j]==False:
dist[i][j]=min(dist[i][j],dist[x][y]+1)
res=max(res,dist[i][j])
visit[i][j]=True
queue.append((i,j))
return res
首先先贴一个不是数学方法的方法(毕竟如果是第一次遇见不知道这个数学方法怎么办!)
其实就是用数组记录,如果读取到这个位置就pop出来。
class Solution:
def lastRemaining(self, n: int, m: int) -> int:
arr=[i for i in range(n)]
num=(m-1)%len(arr)
while len(arr)!=1:
arr.pop(num)
num=(num+(m-1))%len(arr)
return arr[0]
因为借助了系统已经写好的pop方法,但很明显这样做时间效率很低。
方法二:数学方法
写了半天也没弄清楚,看过答案才知道要用约瑟夫环(??什么东西),然后去补习一波,
借鉴大佬写的,这是我看能看懂的一篇!
class Solution:
def lastRemaining(self, n: int, m: int) -> int:
num=0
for i in range(2,n+1):
num=(num+m)%i
return num
这道题没什么难的,主要是练习各种排序问题的写法。这些模版需要好好理解背过。
1.快排
class Solution:
def sortArray(self, nums: List[int]) -> List[int]:
left=0
right=len(nums)-1
self.quickSort(nums,left,right)
return nums
def quickSort(self,nums,left,right):
if left<right:
pi=self.partition(nums,left,right)
self.quickSort(nums,left,pi)
self.quickSort(nums,pi+1,right)
def partition(self,nums,left,right):
num=nums[left]
while left<right:
while left<right and nums[right]>=num:
right-=1
nums[left]=nums[right]
while left<right and nums[left]<=num:
left+=1
nums[right]=nums[left]
nums[left]=num
return left
class Solution:
def sortArray(self, nums: List[int]) -> List[int]:
nums=self.merge_sort(nums)
return nums
def merge_sort(self,nums):
if len(nums)==1:
return nums
mid=len(nums)//2
left=nums[:mid]
right=nums[mid:] #从第m+1个元素取到最后
return self.merge(self.merge_sort(left),self.merge_sort(right))
def merge(self,left,right):
res=[]
i,j=0,0
while i<len(left) and j<len(right):
if left[i]<right[j]:
res.append(left[i])
i+=1
else:
res.append(right[j])
j+=1
res+=left[i:]
res+=right[j:]
return res
3.堆排序
原理感觉这个视频讲的很好:
https://www.bilibili.com/video/av18980178?from=search&seid=3518072115040122033
class Solution:
def sortArray(self, nums: List[int]) -> List[int]:
nums=self.heapSort(nums)
return nums
def heapSort(self,nums):
n=len(nums)
#首先是建堆,建成大根堆的模样
for i in range(n//2-1,-1,-1):
self.adjustHeap(nums,i,n)
#开始调整堆,因为堆顶现在是最大元素,所以把堆顶和最后一个元素交换,再对除去最后一个元素的堆进行调整。
for i in range(n-1,-1,-1):
nums[0],nums[i]=nums[i],nums[0]
self.adjustHeap(nums,0,i)
return nums
def adjustHeap(self,nums,start,end):
num=nums[start]
pos=start
childpos=pos*2+1
while childpos<end:
rightpos=childpos+1
if rightpos<end and nums[childpos]<nums[rightpos]:
childpos=rightpos #比较两个孩子结点的大小,让指针指向大一点的那个
if num<nums[childpos]:
nums[pos]=nums[childpos]
pos=childpos
childpos=pos*2+1 #开始进行结点调整,让大的结点往上走
else:
break
nums[pos]=num
4.计数排序
有点哈希思想,统计每个数字出现的次数,然后从小到大输出。
class Solution:
def sortArray(self, nums: List[int]) -> List[int]:
if not nums:
return []
n=len(nums)
min_num=min(nums)
max_num=max(nums)
arr=[0]*(max_num-min_num+1)
for num in nums:
arr[num-min_num]+=1 #arr用来统计原来每个num出现的次数
j=0
for i in range(n):
while arr[j]==0:
j+=1
nums[i]=j+min_num
arr[j]-=1
return nums
可以看到时间是最快的了吧
5.桶排序
这道题桶排序不明显(有点类似于计数排序的加强版,现根据桶的要求归类,再把每个桶内进行排序),这里贴一下大佬写的代码吧。
https://leetcode-cn.com/problems/sort-an-array/solution/python-shi-xian-de-shi-da-jing-dian-pai-xu-suan-fa/
def bucket_sort(nums, bucketSize):
if len(nums) < 2:
return nums
_min = min(nums)
_max = max(nums)
# 需要桶个数
bucketNum = (_max - _min) // bucketSize + 1
buckets = [[] for _ in range(bucketNum)]
for num in nums:
# 放入相应的桶中
buckets[(num - _min) // bucketSize].append(num)
res = []
for bucket in buckets:
if not bucket: continue
if bucketSize == 1:
res.extend(bucket)
else:
# 当都装在一个桶里,说明桶容量大了
if bucketNum == 1:
bucketSize -= 1
res.extend(bucket_sort(bucket, bucketSize))
return res