格式:
题号+题名+简单思路+code
T1: 两数之和
- 不考虑去重,只找出第一个解;使用哈希表记录出现过的数字,O(N)时间复杂度
func twoSum(nums []int, target int) []int {
if len(nums)==0 {
return nil
}
tmp:=make(map[int]int,len(nums))
for i,v := range nums {
if j,ok:=tmp[target-v];ok {
return []int{j,i}
}
tmp[v]=i
}
return nil
}
- 哈希法能找出无序数组中所有和为定值的组合;但不能去重
※T560:和为K的子数组
- 利用哈希字典找出所有和为K的子数组
- 类似两数之和(两数之差),先转化成前缀和;前缀和用于处理数组区间问题
sum[j]-k=sum[i]
func subarraySum(nums []int, k int) int {
memo:=make([]int,len(nums)+1)
for i:=1;i<len(memo);i++ {
memo[i]=memo[i-1]+nums[i-1]
}
count:=0
dict:=make(map[int]int,0)
for i:=0;i<len(memo);i++ { // 注意i从0开始
if c,ok:=dict[memo[i]-k];ok {
count+=c
}
dict[memo[i]]++
}
return count
}
剑指57:和为s的连续正数序列
- 同样对于连续子数组和我们转化为前缀和数组
- 可以使用哈希表,也可以因为前缀和是有序数组,使用滑动窗口
- 哈希表
class Solution:
def findContinuousSequence(self, target: int) -> List[List[int]]:
if target<=2:
return []
tmp=[i for i in range((target+1)//2+1)]
pre=0
for i in range(len(tmp)):
tmp[i]=pre+tmp[i]
pre=tmp[i]
memo=dict()
ans=[]
for i in range(len(tmp)):
if tmp[i]-target in memo:
ans.append([j for j in range(memo[tmp[i]-target]+1, i+1)])
memo[tmp[i]]=i
return ans
- 滑动窗口
class Solution:
def findContinuousSequence(self, target: int) -> List[List[int]]:
if target<=2:
return []
tmp=[i for i in range((target+1)//2+1)]
pre=0
for i in range(len(tmp)):
tmp[i]=pre+tmp[i]
pre=tmp[i]
i=0
ans=[]
for j in range(len(tmp)):
if tmp[j]-tmp[i]<=target:
pass
else:
while j>i and tmp[j]-tmp[i]>target:
i+=1
if tmp[j]-tmp[i]==target:
ans.append([k for k in range(i+1,j+1)])
return ans
T15:三数之和
- 考虑去重,所有解;使用排序+双指针;最坏情况为O(N^2)时间复杂度
import "sort"
func threeSum(nums []int) [][]int {
target:=0
ans:=[][]int{}
sort.Ints(nums)
for i:=0;i<len(nums)-2;i++ {
if nums[i]>target {
break
}
if i>0 && nums[i]==nums[i-1] {
continue
}
lo:=i+1
hi:=len(nums)-1
a:=nums[i]
for lo<hi && lo>=i+1 && hi<=len(nums)-1 {
b:=nums[lo]
c:=nums[hi]
if a+b+c > target {
hi--
} else if a+b+c < target {
lo++
} else {
ans=append(ans,[]int{a,b,c})
for lo<hi && nums[hi-1]==nums[hi]{
hi--
}
for lo<hi && nums[lo+1]==nums[lo] {
lo++
}
lo++
hi--
}
}
}
return ans
}
- python3
class Solution:
def threeSum(self, nums: List[int]) -> List[List[int]]:
nums=sorted(nums)
ans=[]
for i in range(len(nums)-2):
if i>0 and nums[i]==nums[i-1]:
continue
x=nums[i]
left=i+1
right=len(nums)-1
while left<right:
if x+nums[left]+nums[right]>0:
while left<right and nums[right-1]==nums[right]:
right-=1
right-=1
elif x+nums[left]+nums[right]<0:
while left<right and nums[left+1]==nums[left]:
left+=1
left+=1
else:
ans.append([x, nums[left], nums[right]])
while left<right and nums[left+1]==nums[left]:
left+=1
left+=1
while left<right and nums[right-1]==nums[right]:
right-=1
right-=1
return ans
T11:盛最多的水的容器
- 双指针+短板效应;O(N)时间复杂度
func maxArea(height []int) int {
n:=len(height)
lo:=0
hi:=n-1
maxA:=0
maxH:=0
for lo<hi {
if height[lo]<height[hi] {
tmp:=(hi-lo)*height[lo]
if tmp> maxA {
maxA=tmp
}
maxH=height[lo]
for lo<hi && height[lo+1]<=maxH {
lo++
}
lo++
} else {
tmp:=(hi-lo)*height[hi]
if tmp>maxA {
maxA=tmp
}
maxH=height[hi]
for lo<hi && height[hi-1]<=maxH {
hi--
}
hi--
}
}
return maxA
}
T16:最接近的三数之和
- 由于计算的是和,所以无需考虑重复性;排序+双指针;O(N^2)时间复杂度
import "math"
import "sort"
func threeSumClosest(nums []int, target int) int {
sort.Ints(nums)
tmp:=math.Inf(0)
var ans int
for i:=0;i<len(nums)-2;i++ {
if i>0 && nums[i]==nums[i-1] {
continue
}
t:=nums[i]
lo:=i+1
hi:=len(nums)-1
for lo<hi {
currVal:=t+nums[lo]+nums[hi]-target
c:=math.Abs(float64(currVal))
if c<tmp {
tmp=c
ans=currVal+target
}
if currVal>0 {
hi--
} else if currVal<0 {
lo++
} else {
return target
}
}
}
return ans
}
T739:每日温度
- 单调栈(递减栈)
class Solution:
def dailyTemperatures(self, T: List[int]) -> List[int]:
T.append(float("inf"))
stack=[]
ans=[0 for i in range(len(T))]
for idx in range(len(T)):
while len(stack)>0 and T[stack[-1]]<T[idx]:
tmp=stack.pop()
ans[tmp]=0 if idx==len(T)-1 else idx-tmp
stack.append(idx)
return ans[:-1]
func dailyTemperatures(T []int) []int {
stack:=[]int{}
ans:=make([]int, len(T))
for i:=0;i<len(T);i++ {
for len(stack)>0 && T[stack[len(stack)-1]]<T[i] {
ans[stack[len(stack)-1]]=i-stack[len(stack)-1]
stack=stack[:len(stack)-1]
}
stack=append(stack, i)
}
for i:=0;i<len(stack);i++ {
ans[stack[i]]=0
}
return ans
}
※T316:去除重复字母
- 维持一个单增栈
- 出栈时需要保证相同字母在后续也会出现,对于stack中记录的字母就略过
func removeDuplicateLetters(s string) string {
position:=[26]int{}
memo:=[26]bool{}
for i:=0;i<len(s);i++ {
position[s[i]-'a']=i
}
stack:=[]byte{}
for i:=0;i<len(s);i++ {
if memo[s[i]-'a'] {
continue
}
for len(stack)>0 && stack[len(stack)-1]>s[i] && position[stack[len(stack)-1]-'a']>i {
x:=stack[len(stack)-1]
stack=stack[:len(stack)-1]
memo[x-'a']=false
}
stack=append(stack, s[i])
memo[s[i]-'a']=true
}
return string(stack)
}
T84:柱状图中的最大矩形
- 单调栈;适用于解决如找到第一个比其大/小这样的遍历问题;将O(N^2)→O(N)
- 如单边的数矮个问题/升温天数T739和双边的T84
- 保证最后一个元素出栈
- 更新最值
- 双边扩展需要在栈中加辅助数-1 / re-index
- 使用倒数第二个索引来明确正确左边界
class Solution {
public int largestRectangleArea(int[] heights) {
int ans = 0;
Stack<Integer> stack = new Stack<>();
for (int i=0;i<heights.length;i++) {
while (stack.size()>0 && heights[stack.peek()] > heights[i]) {
int x = stack.peek();
stack.pop();
int s = stack.size()>0?stack.peek():-1;
ans = Math.max(ans, heights[x]*(i-s-1));
}
stack.push(i);
}
while (stack.size()>0) {
int x = stack.peek();
stack.pop();
int s = stack.size()>0?stack.peek():-1;
ans = Math.max(ans, heights[x]*(heights.length-s-1));
}
return ans;
}
}
- re-index
type Stack []int
func (s *Stack) Push(i int) {
*s=append(*s,i)
}
func (s *Stack) Pop() int {
i:=(*s)[len(*s)-1]
*s=(*s)[:len(*s)-1]
return i
}
func (s Stack) Peek() int {
return s[len(s)-1]
}
func largestRectangleArea(heights []int) int {
flag:=-1
maxVal:=0
currIdx:=0
heights=append(heights,flag)
stack:=Stack{}
for idx,val:=range heights {
if len(stack)==0 || heights[stack.Peek()]<=val {
stack.Push(idx)
} else {
for len(stack)>0 && heights[stack.Peek()]>val {
currIdx=stack.Pop()
tmp:=(idx-currIdx)*heights[currIdx]
if tmp>maxVal {
maxVal=tmp
}
}
stack.Push(currIdx)
heights[currIdx]=val
}
}
return maxVal
}
T85:最大矩形
- 将该题看作m个柱状图最大矩形问题求全局最值
- 单调栈的第一种写法,使用栈中倒数第二个索引来计算左边界
class Solution {
public int maximalRectangle(char[][] matrix) {
if (matrix.length==0) return 0;
int ans = 0;
int[] heights = new int[matrix[0].length];
for (int i=0;i<matrix.length;i++) {
for (int j=0;j< heights.length;j++) {
if (matrix[i][j]=='1') heights[j]++;
else heights[j]=0;
}
Stack<Integer> stack = new Stack<>();
for (int j=0;j<heights.length;j++) {
while (stack.size()>0 && heights[stack.peek()]>heights[j]) {
int idx2 = stack.pop();
int idx1 = stack.size()>0?stack.peek():-1;
ans = Math.max(ans, (j-idx1-1)* heights[idx2]);
}
stack.push(j);
}
for (int j=0;j<stack.size();j++) {
int idx2 = stack.get(j);
int idx1 = j>0?stack.get(j-1):-1;
ans = Math.max(ans, (heights.length-idx1-1)*heights[idx2]);
}
}
return ans;
}
}
※T986:区间列表交集
- 双指针;最小区间右端点;O(M+N)时间复杂度
- 同样的逻辑不能改成左端点
- 类似归并排序最后的归并操作
func intervalIntersection(A [][]int, B [][]int) [][]int {
point1:=0
point2:=0
ans:=[][]int{}
for point1<len(A) && point2<len(B) {
list1:=A[point1]
list2:=B[point2]
left:=max(list1[0], list2[0])
if list1[1]<list2[1] {
if list1[1]>=list2[0] {
ans=append(ans, []int{left, list1[1]})
}
point1++
} else {
if list1[0]<=list2[1] {
ans=append(ans, []int{left, list2[1]})
}
point2++
}
}
return ans
}
func max(i int, j int) int {
if i>j {
return i
} else {
return j
}
}
T56: 合并区间
- 区间问题,先排序
- 从相交区间中扩展
min_start
和max_end
import "sort"
func merge(intervals [][]int) [][]int {
if len(intervals)==0 {
return nil
}
ans:=[][]int{}
sort.Slice(intervals, func(i int, j int) bool {
return intervals[i][0]<intervals[j][0]
})
min_start:=intervals[0][0]
max_end:=intervals[0][1]
for i:=1;i<len(intervals);i++ {
if intervals[i][0]<=max_end {
if max_end<intervals[i][1] {
max_end=intervals[i][1]
}
} else {
ans=append(ans,[]int{min_start,max_end})
min_start=intervals[i][0]
max_end=intervals[i][1]
}
}
ans=append(ans,[]int{min_start,max_end})
return ans
}
※T239:滑动窗口的最大值
- 使用单调队列的结构,保持窗口内降序,这样取最大值为O(1)
- 存放的是索引值,首先将不在窗口内的删除,再删除窗口内小于当前值的所有值,最后插入当前值,并输出窗口中第一个值作为当前最大值
- 平均时间复杂度为O(N)
class Solution:
def maxSlidingWindow(self, nums: List[int], k: int) -> List[int]:
ans=[]
window=[]
for i,num in enumerate(nums):
while len(window)>0 and nums[window[-1]]<num:
window.pop()
window.append(i)
if i>=k-1:
ans.append(nums[window[0]])
if window[0]==i-k+1:
window=window[1:]
return ans
T717:1bit和2bit字符
- 回溯法
func isOneBitCharacter(bits []int) bool {
if len(bits)==0 {
return false
}
if len(bits)<2 {
return true
}
if bits[len(bits)-2]==1 {
return !isOneBitCharacterAssist(bits, len(bits)-3)
}
return true
}
func isOneBitCharacterAssist(bits []int, index int) bool {
if index==-1 {
return true
}
if bits[index]==0 {
if isOneBitCharacterAssist(bits, index-1) {
return true
}
if index>0 && bits[index-1]==1 {
if isOneBitCharacterAssist(bits, index-2) {
return true
}
}
} else if index>0 && bits[index]==1 && bits[index-1]==1 {
if isOneBitCharacterAssist(bits, index-2) {
return true
}
} else {
return false
}
return false
}
- 数学法
func isOneBitCharacter(bits []int) bool {
step:=0
for i:=0;i<len(bits);i+=step {
if bits[i]==1 {
step=2
} else {
step=1
}
}
return step==1
}
T832:翻转图像
- O(MN)时间复杂度
func flipAndInvertImage(A [][]int) [][]int {
for i:=0;i<len(A);i++ {
tmp:=A[i]
for lo,hi:=0,len(tmp)-1;lo<=hi;lo,hi=lo+1,hi-1 {
if lo==hi {
tmp[lo]=1-tmp[lo]
continue
}
if tmp[lo]!=tmp[hi] {
continue
} else {
tmp[lo]=1-tmp[lo]
tmp[hi]=1-tmp[hi]
}
}
}
return A
}
T232:用栈实现队列
- 使用两个栈(两次反序)来达到正序
class MyQueue:
def __init__(self):
"""
Initialize your data structure here.
"""
self.stack1=[]
self.stack2=[]
def push(self, x: int) -> None:
"""
Push element x to the back of queue.
"""
self.stack2.append(x)
def pop(self) -> int:
"""
Removes the element from in front of queue and returns that element.
"""
if len(self.stack1)==0:
while len(self.stack2)>0:
self.stack1.append(self.stack2.pop())
if len(self.stack1)>0:
return self.stack1.pop()
return -1
def peek(self) -> int:
"""
Get the front element.
"""
if len(self.stack1)==0:
while len(self.stack2)>0:
self.stack1.append(self.stack2.pop())
if len(self.stack1)>0:
return self.stack1[-1]
return -1
def empty(self) -> bool:
"""
Returns whether the queue is empty.
"""
return len(self.stack1)==0 and len(self.stack2)==0
# Your MyQueue object will be instantiated and called as such:
# obj = MyQueue()
# obj.push(x)
# param_2 = obj.pop()
# param_3 = obj.peek()
# param_4 = obj.empty()
T225:用队列实现栈
- 两个队列,使用O(N)时间复杂度互相转移实现反序
type MyStack struct {
q1 []int
q2 []int
item int
length int
}
/** Initialize your data structure here. */
func Constructor() MyStack {
return MyStack{[]int{}, []int{}, -1, 0}
}
/** Push element x onto stack. */
func (this *MyStack) Push(x int) {
if len(this.q1)==0 {
this.q1=append(this.q1, x)
for idx:=0;idx<this.length;idx++ {
x:=this.q2[0]
this.q1=append(this.q1, x)
this.q2=this.q2[1:]
}
} else if len(this.q2)==0 {
this.q2=append(this.q2, x)
for idx:=0;idx<this.length;idx++ {
x:=this.q1[0]
this.q2=append(this.q2, x)
this.q1=this.q1[1:]
}
}
this.item=x
this.length++
}
/** Removes the element on top of the stack and returns that element. */
func (this *MyStack) Pop() int {
var rVal int
if len(this.q1)==0 {
rVal=this.q2[0]
this.q2=this.q2[1:]
if len(this.q2)>0 {
this.item=this.q2[0]
}
} else if len(this.q2)==0 {
rVal=this.q1[0]
this.q1=this.q1[1:]
if len(this.q1)>0 {
this.item=this.q1[0]
}
}
this.length--
return rVal
}
/** Get the top element. */
func (this *MyStack) Top() int {
return this.item
}
/** Returns whether the stack is empty. */
func (this *MyStack) Empty() bool {
return this.length==0
}
/**
* Your MyStack object will be instantiated and called as such:
* obj := Constructor();
* obj.Push(x);
* param_2 := obj.Pop();
* param_3 := obj.Top();
* param_4 := obj.Empty();
*/
- 用一个队列,通过O(N)时间复杂度,类似插入排序实现反序
type MyStack struct {
q1 []int
}
/** Initialize your data structure here. */
func Constructor() MyStack {
return MyStack{[]int{}}
}
/** Push element x onto stack. */
func (this *MyStack) Push(x int) {
this.q1=append(this.q1, x)
idx:=len(this.q1)-1
for idx>0 {
this.q1[idx], this.q1[idx-1]=this.q1[idx-1], this.q1[idx]
idx--
}
}
/** Removes the element on top of the stack and returns that element. */
func (this *MyStack) Pop() int {
x:=this.q1[0]
this.q1=this.q1[1:]
return x
}
/** Get the top element. */
func (this *MyStack) Top() int {
return this.q1[0]
}
/** Returns whether the stack is empty. */
func (this *MyStack) Empty() bool {
return len(this.q1)==0
}
/**
* Your MyStack object will be instantiated and called as such:
* obj := Constructor();
* obj.Push(x);
* param_2 := obj.Pop();
* param_3 := obj.Top();
* param_4 := obj.Empty();
*/
T20:有效的括号
- 括号问题→栈
var memo map[byte]byte = map[byte]byte{')':'(', '}':'{', ']':'['}
func isValid(s string) bool {
stack:=[]byte{}
for i:=0;i<len(s);i++ {
if b, ok:=memo[s[i]];ok {
if len(stack)==0 || stack[len(stack)-1]!=b {
return false
}
stack=stack[:len(stack)-1]
} else {
stack=append(stack, s[i])
}
}
if len(stack)>0 {
return false
}
return true
}
T42:接雨水
-
s u m = s u m + min { m a x H [ 0 : i ] , m a x H [ i : n − 1 ] } − H [ i ] sum=sum+\min\{maxH_{[0:i]},maxH_{[i:n-1]}\}-H[i] sum=sum+min{maxH[0:i],maxH[i:n−1]}−H[i]
-
记忆化递归;O(N)时间复杂度;O(N)空间复杂度
func trap(height []int) int {
memoL:=make([]int,len(height))
for i:=0;i<len(memoL);i++ {
memoL[i]=-1
}
memoR:=make([]int,len(height))
for i:=0;i<len(memoR);i++ {
memoR[i]=-1
}
sum:=0
for i:=0;i<len(height);i++ {
i1:=getMaxL(height, i, memoL)
i2:=getMaxR(height, i, memoR)
if i1>i2 {
sum+=i2-height[i]
} else {
sum+=i1-height[i]
}
}
return sum
}
func getMaxL(height []int, loc int, memoL []int) int {
if loc==0 {
memoL[0]=height[0]
return height[0]
}
if memoL[loc]!=-1 {
return memoL[loc]
}
tmp:=getMaxL(height, loc-1, memoL)
if tmp>height[loc] {
memoL[loc]=tmp
return tmp
} else {
memoL[loc]=height[loc]
return height[loc]
}
}
func getMaxR(height []int, loc int, memoR []int) int {
if loc==len(height)-1 {
memoR[loc]=height[loc]
return height[loc]
}
if memoR[loc]!=-1 {
return memoR[loc]
}
tmp:=getMaxR(height, loc+1, memoR)
if tmp>height[loc] {
memoR[loc]=tmp
return tmp
} else {
memoR[loc]=height[loc]
return height[loc]
}
}
- s u m = s u m + min { m a x H [ 0 : l o ] , m a x H [ h i : n − 1 ] } − H [ i ∈ { l o , h i } ] sum=sum+\min\{maxH_{[0:lo]},maxH_{[hi:n-1]}\}-H[i\in\{lo,hi\}] sum=sum+min{maxH[0:lo],maxH[hi:n−1]}−H[i∈{lo,hi}]
- 短板效应
- O(N)时间复杂度;O(1)空间复杂度
func trap(height []int) int {
if len(height)==0 {
return 0
}
ans:=0
left:=height[0]
right:=height[len(height)-1]
point1:=0
point2:=len(height)-1
for point1<point2 {
if left<right {
if height[point1+1]<=left {
ans+=left-height[point1+1]
} else {
left=height[point1+1]
}
point1++
} else {
if height[point2-1]<=right {
ans+=right-height[point2-1]
} else {
right=height[point2-1]
}
point2--
}
}
return ans
}
※T224:基本计算器
- 利用栈存储之前的计算结果
- + , − , ∗ , / , ( , ) +, -, *, /, (, ) +,−,∗,/,(,)
import "unicode"
func calculate(s string) int {
seq:=[]rune(s)
idx:=0
return calculateAssist(seq, &idx)
}
func calculateAssist(seq []rune, idx *int) int {
num:=0
sign:='+'
ans:=0
stack:=[]int{}
for *idx<len(seq) {
c:=seq[*idx]
if unicode.IsNumber(c) {
num=num*10+int(c-'0')
} else if c=='(' {
(*idx)++
num=calculateAssist(seq, idx)
} else if !unicode.IsSpace(c) { // 运算符或者')'或者'.'
if c==')' {
break
}
switch sign {
case '+':
stack=append(stack, num)
case '-':
stack=append(stack, -num)
case '*':
stack[len(stack)-1]*=num
case '/':
stack[len(stack)-1]/=num
}
sign=c
num=0
}
(*idx)++
}
switch sign { // 末尾数字
case '+':
stack=append(stack, num)
case '-':
stack=append(stack, -num)
case '*':
stack[len(stack)-1]*=num
case '/':
stack[len(stack)-1]/=num
}
for i:=0;i<len(stack);i++ {
ans+=stack[i]
}
return ans
}
T394:字符串解码
- 十分类似于基本计算器的思路,即对括号的处理上
import "unicode"
import "bytes"
func decodeString(s string) string {
seq:=[]rune(s)
idx:=0
return decodeStringAssist(seq, &idx)
}
func decodeStringAssist(seq []rune, idx *int) string {
var tmp bytes.Buffer
num:=0
for *idx < len(seq) {
c:=seq[*idx]
if unicode.IsNumber(c) { // 数字
num=num*10+int(c-'0')
} else if unicode.IsLetter(c) { // 字母
tmp.WriteRune(c)
} else if c=='[' { // '['代替了括号和乘法
*(idx)++
ans:=decodeStringAssist(seq, idx)
for i:=0;i<num;i++ {
tmp.WriteString(ans)
}
num=0
} else { // ']'表示终止
break
}
*(idx)++
}
return tmp.String()
}
剑指04:二维数组中的查找
- 相当于构造一棵二叉搜索树
func findNumberIn2DArray(matrix [][]int, target int) bool {
i:=len(matrix)-1
j:=0
for i>=0 && j<len(matrix[0]) {
if matrix[i][j]<target {
j++
} else if matrix[i][j]>target {
i--
} else {
return true
}
}
return false
}
※剑指45:把数组排成最小的数
- 排序判断:任意交换相邻两个元素的位置都会使结果增大
- 解决 3、32、34的顺序
func minNumber(nums []int) string {
str_nums:=make([]string, len(nums))
for i:=0;i<len(nums);i++ {
str_nums[i]=strconv.Itoa(nums[i])
}
sort.Slice(str_nums, func(i int, j int) bool {
return str_nums[i]+str_nums[j]<=str_nums[j]+str_nums[i]
})
return strings.Join(str_nums, "")
}
※剑指31:判断栈的压入序列、弹出序列
class Solution:
def validateStackSequences(self, pushed: List[int], popped: List[int]) -> bool:
stack, i = [], 0
for num in pushed:
stack.append(num) # num 入栈
while stack and stack[-1] == popped[i]: # 循环判断与出栈
stack.pop()
i += 1
return not stack
func validateStackSequences(pushed []int, popped []int) bool {
stack:=[]int{}
index:=0
for i:=0;i<len(pushed);i++ {
stack=append(stack, pushed[i])
for len(stack)>0 && stack[len(stack)-1]==popped[index] {
stack=stack[:len(stack)-1]
index++
}
}
if len(stack)==0 {
return true
}
return false
}
※剑指51:数组中的逆序对
- 基于归并排序思想
- 在对数组进行归并排序的过程中,将无序变为有序的过程即可统计逆序对
func reversePairs(nums []int) int {
tmp:=make([]int, len(nums))
return mergeSort(nums, tmp, 0, len(nums)-1)
}
func mergeSort(nums []int, tmp []int, lo int, hi int) int {
if lo>=hi {
return 0
}
mid:=(hi-lo)/2+lo
num1:=mergeSort(nums, tmp, lo, mid)
num2:=mergeSort(nums, tmp, mid+1, hi)
if nums[mid]<=nums[mid+1] { // 此时已有序
return num1+num2
}
count:=0
for i:=lo;i<=hi;i++ {
tmp[i]=nums[i]
}
i:=lo
j:=mid+1
for k:=lo;k<=hi;k++ {
if i==mid+1 {
nums[k]=tmp[j]
j++
} else if j==hi+1 {
nums[k]=tmp[i]
i++
} else {
if tmp[i]<=tmp[j] { // 稳定排序
nums[k]=tmp[i]
i++
} else {
nums[k]=tmp[j]
j++
count+=mid-i+1
}
}
}
return num1+num2+count
}
T31:下一个排列
- 若当前排列降序,则逆序输出
- 否则找到第一个nums[i-1]<nums[i]的位置i,将nums[i-1]与nums[i:]上第一个比nums[i-1]大的元素交换位置,然后将nums[i:]逆序后输出
- 时间复杂度O(N),空间复杂度O(1)
class Solution {
public void nextPermutation(int[] nums) {
if (nums.length<2) {
return;
}
for (int point=nums.length-1;point>=1;point--) {
if (nums[point-1]<nums[point]) {
for (int index=nums.length-1;index>=point;index--) {
if (nums[index]>nums[point-1]) {
int tmp=nums[index];
nums[index]=nums[point-1];
nums[point-1]=tmp;
reverse(nums, point, nums.length-1);
return;
}
}
}
}
reverse(nums, 0, nums.length-1);
}
public void reverse(int[] nums, int start, int end) {
for (;start<end;start++,end--) {
int tmp=nums[start];
nums[start]=nums[end];
nums[end]=tmp;
}
}
}
T60:第k个排列
- 注意java中静态变量和实例变量都能用于预计算
- 通过模除得到当前应取值
class Solution {
private static int[] memo = produceNum();
public String getPermutation(int n, int k) {
k--;
int num = n;
char[] ans = new char[n];
boolean[] set = new boolean[n];
while (num>=1) {
int index = k/memo[num-1];
k %= memo[num-1];
int tmp=0;
for (int i=1;i<=n;i++) {
if (set[i-1]) {
continue;
}
if (tmp==index) {
set[i-1]=true;
ans[n-num]=(char)(i+'0');
break;
}
tmp++;
}
num--;
}
return new String(ans);
}
private static int[] produceNum() {
int[] memo = new int[10];
memo[0]=1;
for (int i=1;i<memo.length;i++) {
memo[i]=i*memo[i-1];
}
return memo;
}
}
T75:颜色分类
- 0/1/2 三向切分快排
- 如何指定基准值的具体值
- 异或法实现swap对数组交换时当取同一个值时出错
class Solution {
public void sortColors(int[] nums) {
for (int i=0;i< nums.length;i++) {
if (nums[i]==1) {
swap(nums, 0, i);
}
}
quickSortTri(nums);
}
public void quickSortTri(int[] nums) {
int lo = 0;
int idx = lo+1;
int hi = nums.length-1;
int tmp = nums[lo];
while (idx <= hi) {
int c = Integer.compare(nums[idx], tmp);
if (c<0) {
swap(nums, lo++, idx++);
} else if (c>0) {
swap(nums, hi--, idx);
} else {
idx++;
}
}
}
public void swap(int[] nums, int idx1, int idx2) {
int tmp = nums[idx1];
nums[idx1] = nums[idx2];
nums[idx2] = tmp;
}
}
T1207:独一无二的出现次数
class Solution {
public boolean uniqueOccurrences(int[] arr) {
HashMap<Integer, Integer> counter = new HashMap<>();
for (Integer i:arr) {
counter.put(i, counter.getOrDefault(i, 0)+1);
}
HashSet<Integer> set = new HashSet<>();
for (Integer i :counter.values()) {
if (set.contains(i)) {
return false;
}
set.add(i);
}
return true;
}
}
T80:删除排序数组中的重复项Ⅱ
- 排序+双指针 去重
class Solution {
public int removeDuplicates(int[] nums) {
if (nums.length==0) {
return 0;
}
int point1 = 0, point2 = 0;
while (point1 < nums.length) {
int count = 0;
while (point1 < nums.length && nums[point1] == nums[point2]) {
if (count==1) { // 给出最高重复数
nums[++point2] = nums[point1];
}
count++;
point1++;
}
if (point1 < nums.length) {
nums[++point2]=nums[point1];
}
}
return point2+1;
}
}