Day1 两数相加
给出两个 非空 的链表用来表示两个非负的整数。其中,它们各自的位数是按照 逆序 的方式存储的,并且它们的每个节点只能存储 一位 数字。
如果,我们将这两个数相加起来,则会返回一个新的链表来表示它们的和。
您可以假设除了数字 0 之外,这两个数都不会以 0 开头。
示例:
输入:(2 -> 4 -> 3) + (5 -> 6 -> 4)
输出:7 -> 0 -> 8
原因:342 + 465 = 807
解题思路
本题即是自己实现进位
定义一个进位状态符
fCarry = 0
开始循环
对链表A取节点Ai, 对链表B取节点Bi若Ai,Bi不存在,且fCarry==0 跳出循环
定义变量n = fCarry;
fCarry = 0;若Ai存在,则 n = n + Ai
若Bi存在,则 n = n + BifCarry = n / 10
n = n - fCarry*10
对链表C插入新节点n
GO实现代码
/**
* Definition for singly-linked list.
* type ListNode struct {
* Val int
* Next *ListNode
* }
*/
func addTwoNumbers(A *ListNode, B *ListNode) *ListNode {
if nil == A || nil == B {
return nil
}
fCarry := 0
var result *ListNode = &ListNode{Val:0, Next:nil, }
var C *ListNode = result
for {
Ai := A
Bi := B
if nil == Ai && nil == Bi && 0 == fCarry{
break;
}
n := fCarry
fCarry = 0
if nil != Ai {
n = n + (*Ai).Val
}
if nil != Bi {
n = n + (*Bi).Val
}
fCarry = n / 10
n = n % 10
(*C).Next = &ListNode{ Val:n, Next:nil}
C = (*C).Next
if A != nil{
A = (*A).Next
}
if B != nil{
B = (*B).Next
}
}
return (*result).Next
}
Python3实现代码
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def addTwoNumbers(self, l1: ListNode, l2: ListNode) -> ListNode:
carry = 0
result = ListNode(0)
C = result
while l1 or l2 or carry:
Ai = l1.val if l1 else 0
Bi = l2.val if l2 else 0
sum = carry + Ai + Bi
carry = sum // 10
sum = sum % 10
C.next = ListNode(sum)
C= C.next
l1 = l1.next if l1 else None
l2 = l2.next if l2 else None
return result.next
值得一提的是,
- 再python中,
/
法并不是整除,因此要用//
- 在尾部替换l1 l2 (A B)时,要判断是否为
nil
或者None
,就是要用到next操作就一定要保证这个对象的引用有效
Day2 寻找两个有序数组的中位数
给定两个大小为 m 和 n 的有序数组 nums1 和 nums2。
请你找出这两个有序数组的中位数,并且要求算法的时间复杂度为 O(log(m + n))。
你可以假设 nums1 和 nums2 不会同时为空。
示例 1:
nums1 = [1, 3]
nums2 = [2]
则中位数是 2.0
示例 2:
nums1 = [1, 2]
nums2 = [3, 4]
则中位数是 (2 + 3)/2 = 2.5
解题思路,
核心观点:中位数即一个可以将一组数分为左右数目相等的两组树。
对于本题而言,给了两组数A,B,要找到A,B的中位数 m
则m把A切割为了两组数 Aleft, Aright, 把B切割为了两组 Bleft, Bright
则有
len(Aleft) + len(Bleft) = len(Aright) + len(Bright)
举个例子
A=[1,3,5,7,8]
B=[3,8,10,13]则合并后的数组为
C=[1,3,3,5,7,8,8,10,13]
中位数为 7
7把A分割为了
Aleft = [1,3,5,7]
Aright = [7,8]
7把B分割为了
Bleft = [3]
Bright = [8,10,13]显然有
len(Aleft) + len(Bleft) = len(Aright) + len(Bright)
同样的
A=[1,3,5,7,8]
B=[3,8,10,13,14]则合并后的数组为
C=[1,3,3,5,7,8,8,10,13,14]
中位数为 7.5
7.5把A分割为了
Aleft = [1,3,5,7]
Aright = [8]
7把B分割为了
Bleft = [3]
Bright = [8,10,13,14]显然有
len(Aleft) + len(Bleft) = len(Aright) + len(Bright)
因此我们要找出中位数m,即是找出c1和c2
使得c1将A分割为两组数 Aleft, Aright,
c2将B切割为了两组 Bleft, Bright
并且
len(Aleft) + len(Bleft) = len(Aright) + len(Bright)
但仅有这个条件还不够,因为满足这个条件的c1,c2有无数个
因此还需要另一个条件
左边的元素 必然小于右边的元素
即
Aleft < Aright 且 Aleft < Bright
Bleft < Aright 且 Bleft < Bright
寻找 c1, c2 的流程
假设 len(A) > len(B)
h = (len(A)+len(B)+1) / 2 整除
初始化 c1 = len(A)/2
循环c2 = h-c1 (保证c1+c2=h 即总数的一半)
如果 A[c1] > B[c2] 或者 B[c2] > A[c1]c1 = c1 - 1
实现代码 python
class Solution(object):
def findMedianSortedArrays(self, A, B):
m, n = len(A), len(B)
if m > n:
A, B, m, n = B, A, n, m
if n == 0:
raise ValueError
imin, imax, half_len = 0, m, int((m + n + 1) / 2)
print((m + n + 1) / 2, " ", int((m + n + 1) / 2), "\n")
while imin <= imax:
i = int((imin + imax) / 2)
print(((imin + imax) / 2), " ", int((imin + imax) / 2), "\n")
j = half_len - i
if i < m and B[j-1] > A[i]:
# i is too small, must increase it
imin = i + 1
elif i > 0 and A[i-1] > B[j]:
# i is too big, must decrease it
imax = i - 1
else:
# i is perfect
if i == 0: max_of_left = B[j-1]
elif j == 0: max_of_left = A[i-1]
else: max_of_left = max(A[i-1], B[j-1])
if (m + n) % 2 == 1:
return max_of_left
if i == m: min_of_right = B[j]
elif j == n: min_of_right = A[i]
else: min_of_right = min(A[i], B[j])
return (max_of_left + min_of_right) / 2.0
Day3 最长回文子串
主要思路有两种,中心扩展法和Manacher算法
中心扩展法比较简单,这里不再赘述
Manacher算法
reference : https://segmentfault.com/a/1190000008484167?utm_source=tag-newest
思路
数组p[i]表示字符串中,以第i个字符为中心的回文半径
算法描述
mx 为以第id个字符为中心的回文的右端点的下标
如果第i个字符串,i<mx,则p[i] = min(p[2 * id - i], mx - i);
否则
p[i] = 1
对i节点的回文半径重计算
算法为while s_new[i-p[i]] == s_new[i+p[i]]
p[i] ++如果 mx < i+p[i]
mx = i+p[i] 且 id = i
实现代码:
GO
package main
import "fmt"
import "strings"
func longestPalindrome(s string) string {
s_new := initString(s)
n := len(s_new)
p := make([]int, n)
mx := 0
id := 0
max_id := 0
max_r := 1
for i:=1; i<n-1; i=i+1{
if i< mx{
p[i] = Min(p[2*id-i], mx-i)
} else {
p[i] = 1
}
for s_new[i-p[i]] == s_new[i+p[i]] {
p[i]++
}
if mx < i+p[i]{
mx = i+p[i]
id = i
}
if p[i] >= max_r{
max_r = p[i]
max_id = i
}
}
result := s_new[max_id-(max_r-1):max_id+(max_r-1)]
result = strings.Replace(result,"#","",-1)
result = strings.Replace(result,"$","",-1)
result = strings.Replace(result,"&","",-1)
return result
}
func Min(x, y int) int {
if x < y {
return x
}
return y
}
func initString(s string ) string {
n := len(s)
s_new := "$#"
for i:=0;i<n;i=i+1{
s_new += string(s[i])
s_new += "#"
}
s_new += "&" //保证边界不同
return s_new
}
func main(){
s:=longestPalindrome("abbahopxpo")
fmt.Printf(s)
fmt.Printf("\n")
}
Python3 实现代码
class Solution:
def longestPalindrome(self, s: str) -> str:
s_new = "$#"
for c in s:
s_new += c
s_new += "#"
s_new += "&"
n = len(s_new)
mx = 0
id = 0
max_id = 0
max_r = 1
p = [0 for i in range(n)]
for i in range(1,n-1):
if i < mx:
p[i] = min(p[2*id-i], mx-i)
else:
p[i] = 1
while s_new[i-p[i]] == s_new[i+p[i]]:
p[i] += 1
if i+p[i] > mx:
id = i
mx = i+p[i]
if p[i] >= max_r:
max_id = i
max_r = p[i]
result = s_new[max_id-(max_r-1):max_id+(max_r-1)]
result = result.replace("#","").replace("$","").replace("&","")
return result
Day4 整数反转
给出一个 32 位的有符号整数,你需要将这个整数中每位上的数字进行反转。
示例 1:
输入: 123
输出: 321
示例 2:
输入: -123
输出: -321
示例 3:
输入: 120
输出: 21
太过简单,因此不做解析
GO实现代码
func reverse(x int) int {
t := x
y := 0
for 0 != t/10 || 0 != t%10 {
y = y*10 + t%10
t = t/10
}
if powerf3(2,31)-1 < y || 0-powerf3(2,31) > y {
y = 0
}
return y
}
func powerf3(x int, n int) int {
ans := 1
for n != 0 {
ans *= x
n--
}
return ans
}
Python3 实现代码
class Solution:
def reverse(self, x: int) -> int:
flag = -1 if x < 0 else 1
t = x*flag
y = 0
while 0!=t//10 or 0!=t%10:
y = y*10 + t%10
t = t//10
if 2**31-1 < y or 0-2**31>y:
y = 0
return y*flag
需要注意:
1.再python中 **
表示幂
2.类函数调用同类的函数要用 self.function ,必须加 “self.” 不然会默认调用外部的函数
3. python中,对负数不能正确地做 // 整除运算和 % 求余运算
Day5 字符串转换整数
描述
请你来实现一个
atoi
函数,使其能将字符串转换成整数。首先,该函数会根据需要丢弃无用的开头空格字符,直到寻找到第一个非空格的字符为止。
当我们寻找到的第一个非空字符为正或者负号时,则将该符号与之后面尽可能多的连续数字组合起来,作为该整数的正负号;假如第一个非空字符是数字,则直接将其与之后连续的数字字符组合起来,形成整数。
该字符串除了有效的整数部分之后也可能会存在多余的字符,这些字符可以被忽略,它们对于函数不应该造成影响。
注意:假如该字符串中的第一个非空格字符不是一个有效整数字符、字符串为空或字符串仅包含空白字符时,则你的函数不需要进行转换。
在任何情况下,若函数不能进行有效的转换时,请返回 0。
说明:
假设我们的环境只能存储 32 位大小的有符号整数,那么其数值范围为 [−231, 231 − 1]。如果数值超过这个范围,qing返回 INT_MAX (231 − 1) 或 INT_MIN (−231) 。
比较简单 因此不描述算法
GO实现:
import "strings"
import "math"
func myAtoi(str string) int {
str = strings.Trim(str, " ")
n := len(str)
result := 0
flag := 1
for i := 0; i<n; i++{
c := str[i]
if int(c) >= int('0') && int(c) <= int('9'){
var bignum int64 = (int64(result) * 10 + int64(c) - int64('0'))*int64(flag)
if bignum > math.MaxInt32{
return math.MaxInt32
} else if bignum < math.MinInt32{
return math.MinInt32
} else{
result = result * 10 + int(c) - int('0')
}
} else if c == '-' && 0 == i{
flag *= -1
} else if c != '+' || 0 != i{
break
}
}
return flag*result
}
注意,使用int变量,数目累加太大时,会溢出,所以要判断溢出的话,需要用 int64,因此再运算之前,先设置一个临时变量 bignum int64,用于计算接下来的运算结果会不会溢出,如果溢出直接返回最大最小值
python
class Solution:
def myAtoi(self, str: str) -> int:
flag = 1
result = 0
id = 0
str = str.strip()
for a in str:
if ord(a) >= ord('0') and ord(a) <= ord('9'):
result = result * 10 + ord(a) - ord('0')
elif a == '-' and 0 == id:
flag = -1*flag
elif a == '+' and 0 == id:
pass
else:
break
id += 1
result = min(result*flag,2**31-1) if result * flag > 0 else max(result*flag,-2**31)
return result
注意python中, int(char) 不能得到char的ASCII字符,而要用 ord() 函数,相反,ascii码转字符,则用 chr()
Day6 回文数
判断一个整数是否是回文数。回文数是指正序(从左向右)和倒序(从右向左)读都是一样的整数。
示例 1:
输入: 121
输出: true
示例 2:
输入: -121
输出: false
解释: 从左向右读, 为 -121 。 从右向左读, 为 121- 。因此它不是一个回文数。
示例 3:
输入: 10
输出: false
解释: 从右向左读, 为 01 。因此它不是一个回文数。
进阶:
你能不将整数转为字符串来解决这个问题吗?
GO实现:
func isPalindrome(x int) bool {
if x < 0{
return false
}
y := 0
t := x
for 0 != x / 10 || 0 != x % 10{
y = y*10 + x%10
x = x / 10
}
if t == y{
return true
}
return false
}
注意,x被拿去变成临时变量了,因此要用一个t 来保存原始值
Python实现
class Solution:
def isPalindrome(self, x: int) -> bool:
if x < 0 :
return False
t = x
y = 0
while t // 10 or t % 10:
y = y * 10 + t%10
t = t // 10
if x == y:
return True
return False
注意,python中的真与假是 True, False
Day7 盛最多水的容器
给定 n 个非负整数 a1,a2,…,an,每个数代表坐标中的一个点 (i, ai) 。在坐标内画 n 条垂直线,垂直线 i 的两个端点分别为 (i, ai) 和 (i, 0)。找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。
说明:你不能倾斜容器,且 n 的值至少为 2。
算法:
双指针法
我们在由线段长度构成的数组中使用两个指针,一个放在开始,一个置于末尾。 此外,我们会使用变量 maxareamaxarea 来持续存储到目前为止所获得的最大面积。 在每一步中,我们会找出指针所指向的两条线段形成的区域,更新 maxareamaxarea,并将指向较短线段的指针向较长线段那端移动一步。
GO实现
func maxArea(height []int) int {
i := 0
j := len(height) - 1
total := 0
max := 0
for i != j{
if height[i] > height[j]{
total = (j-i)*height[j]
j--
}else {
total = (j-i)*height[i]
i++
}
if max < total{
max = total
}
}
return max
}
python实现
class Solution:
def maxArea(self, height: List[int]) -> int:
i = 0
j = len(height) - 1
max_a = 0
while i != j :
h = height[i] if height[i] < height[j] else height[j]
total = h * (j-i)
max_a = total if total > max_a else max_a
if height[i] > height[j] :
j -= 1
else:
i += 1
return max_a
Day8 最长公共前缀
编写一个函数来查找字符串数组中的最长公共前缀。
如果不存在公共前缀,返回空字符串 “”。
示例 1:
输入: ["flower","flow","flight"]
输出: "fl"
示例 2:
输入: ["dog","racecar","car"]
输出: ""
解释: 输入不存在公共前缀。
说明:
所有输入只包含小写字母 a-z 。
GO实现
func longestCommonPrefix(strs []string) string {
if len(strs) == 0{
return ""
}
cpr := strs[0]
for i:=1;i<len(strs);i++{
newcpr := ""
n := 0
if len(cpr) > len(strs[i]){
n = len(strs[i])
}else{
n = len(cpr)
}
for j :=0;j<n;j++{
if cpr[j] != strs[i][j]{
break
}
newcpr += string(cpr[j])
}
cpr = newcpr
}
return cpr
}
Python实现
class Solution:
def longestCommonPrefix(self, strs: List[str]) -> str:
if len(strs) == 0:
return ""
cpr = strs[0]
for i in range(1,len(strs)):
n = min(len(cpr),len(strs[i]))
new_cpr = ""
for j in range(0,n):
if cpr[j]!=strs[i][j]:
break
new_cpr += cpr[j]
cpr = new_cpr
return cpr
注意 for i in range(0,len) 的循环是从 0 到 len - 1
Day9 三数之和
糟糕的思路:
先随机取两个数a,b,然后算出其理论上的第三个数c, 建立哈希表 hash[c] = [a,b,c]
之后遇到c了,直接将其哈希值添加入结果。
思路糟糕因为时间复杂度大,而且要对结果进行排序等操作
Go实现
import (
"fmt"
"reflect"
"sort"
)
func threeSum(nums []int) [][]int {
var result [][]int
for i := 0; i < len(nums)-2; i++ {
hash := make(map[int][]int)
for j := i + 1; j < len(nums); j++ {
if hash[nums[j]] != nil {
sort.Slice(hash[nums[j]], func(a, b int) bool {
return hash[nums[j]][a] < hash[nums[j]][b]
})
result = append(result, hash[nums[j]])
hash[nums[j]] = nil
} else {
c := 0 - nums[i] - nums[j]
hash[c] = []int{nums[i], nums[j], c}
}
}
}
var result_sort [][]int
for id := 0; id < len(result); id++ {
item := result[id]
bappend := true
for i := 0; i < len(result_sort); i++ {
if reflect.DeepEqual(item, result_sort[i]) {
bappend = false
break
} else if item[0] < result_sort[i][0] || (item[0] == result_sort[i][0] && item[1] < result_sort[i][1]) || (item[0] == result_sort[i][0] && item[1] == result_sort[i][1] && item[2] < result_sort[i][2]) {
bappend = false
if 0 == i {
result_sort = append([][]int{item}, result_sort...)
} else {
rear := append([][]int{}, result_sort[i:]...)
result_sort = append(append(result_sort[:i], item), rear...)
}
break
}
}
if bappend {
result_sort = append(result_sort, item)
}
}
return result_sort
}
注意:
其一,再rear := append … 这条语句中
不可以直接写成 rear := result_sort[i:] 因为 这样是赋值引用值,而不是赋值
所以要用 append 新建一个slice值。其二,在append中,如果是[][]int 加入到 [][]int中,可以用 append(a, b…) 省略号的形式
其三,slice排序,可以import “sort” 然后调用 sort.Slice(数组, 比较函数) 无返回值
其四,slice的比较不可以直接 == 比较,必须用 reflect.DeepEqual(a,b)
其五,数组A[id1: id2] 是开闭区间,即 [id1, id2)
Python 实现
class Solution:
def threeSum(self, nums):
result = []
for i in range(0,len(nums)-2):
map = dict()
for j in range(i+1,len(nums)):
if map.get(nums[j]) != None:
map.get(nums[j]).sort()
result.append(map.get(nums[j]))
map[nums[j]] = None
else:
c = 0-nums[i]-nums[j]
map[c] = list([nums[i],nums[j],c])
result_sort = []
for item in result:
append = True
for i in range(0,len(result_sort)):
if item == result_sort[i]:
append = False
break
if item[0] < result_sort[i][0] or (item[0] == result_sort[i][0] and item[1] < result_sort[i][1]) or (item[0] == result_sort[i][0] and item[1] == result_sort[i][1] and item[2] < result_sort[i][2]):
result_sort.insert(i,item)
append = False
break
if append:
result_sort.append(item)
return result_sort
a = Solution()
print(a.threeSum([0,2,2,3,0,1,2,3,-1,-4,2]))
注意
数组A.sort() 不需要返回值,直接调用即可把A排好序
一个更好的实现思路
双指针夹逼法,参考day7的算法。
Go实现
import (
"fmt"
"sort"
)
func threeSum(nums []int) [][]int {
var result [][]int
sort.Slice(nums, func(a, b int) bool {
if nums[a] < nums[b] {
return true
}
return false
})
for i := 0; i < len(nums); i++ {
if i > 0 && nums[i] == nums[i-1] {
continue // 保证不重复
}
right := len(nums) - 1
left := i + 1
for left < right {
sum := nums[i] + nums[left] + nums[right]
if 0 == sum {
result = append(result, []int{nums[i], nums[left], nums[right]})
left++
right--
for nums[left] == nums[left-1] && left < right {
left++
}
for nums[right] == nums[right+1] && left < right {
right--
}
} else if sum > 0 {
right--
} else {
left++
}
}
}
return result
}
python实现
def threeSum(nums):
"""
思路:固定一个数,使这个数其后的两个数和为0,
"""
nums.sort()
n = len(nums)
res = []
i = 0
for i in range(n):
if i == 0 or nums[i]>nums[i-1]: # 第一轮为0,第二轮开始固定的数不能重复
left = i+1
right = n-1
while left < right:
three_sum = nums[i] + nums[left] +nums[right]
if three_sum ==0:
res.append([nums[i], nums[left], nums[right]])
left += 1
right -= 1
while left < right and nums[left] == nums[left-1]: # 左边重复则右移
left += 1
while right > left and nums[right] == nums[right+1]: # 右边重复则左移
right -= 1
elif three_sum > 0: # 三数和大于0,则右边左移
right -=1
else : # 三数和小于0,则左边右移
left +=1
return res
Day10 最接近的三数之和
和day9的实现办法很类似
GO实现
import "sort"
func abs(a int) int {
if a < 0 {
return 0 - a
}
return a
}
func threeSumClosest(nums []int, target int) int {
sort.Slice(nums, func(a, b int) bool {
if nums[a] < nums[b] {
return true
}
return false
})
closest := nums[0] + nums[1] + nums[2]
for i := 0; i < len(nums); i++ {
if i > 0 && nums[i] == nums[i-1] {
continue
}
left := i + 1
right := len(nums) - 1
for left < right {
c := nums[i] + nums[left] + nums[right]
if abs(c-target) < abs(closest-target) {
closest = c
}
if c == target {
return target
} else if c > target {
right--
} else {
left++
}
}
}
return closest
}
Day11 有效的括号
给定一个只包括 ‘(’,’)’,’{’,’}’,’[’,’]’ 的字符串,判断字符串是否有效。
有效字符串需满足:
左括号必须用相同类型的右括号闭合。
左括号必须以正确的顺序闭合。
注意空字符串可被认为是有效字符串。
示例 1:
输入: "()"
输出: true
示例 2:
输入: "()[]{}"
输出: true
示例 3:
输入: "(]"
输出: false
示例 4:
输入: "([)]"
输出: false
示例 5:
输入: "{[]}"
输出: true
Go实现
func isValid(s string) bool {
hash := map[rune]rune{'(': ')', '{': '}', '[': ']'}
stack := make([]rune, len(s))
top := -1
for _, c := range s {
if c == '(' || c == '{' || c == '[' {
top++
stack[top] = c
} else if c == ')' || c == '}' || c == ']' {
if top < 0 {
return false
}
cpr := stack[top]
top--
if hash[cpr] != c {
return false
}
}
}
if top >= 0 {
return false
}
return true
}
注意 rune类型即是string的单元,也就相对于一个char(但是rune是unicode的)
rune用单引号
声明一个map就是 map[type]type{ a:b, c:d, e:f }
slice 的创建值得注意
如果 stack := make([]rune, 1) 的话,
由于这个slick长度为1,那么append进去的元素将排在第二位
另外 stack := make([]rune, len, cap) ,其中cap可以不填,如果cap小于len,运行时会出错
使用slice作为stack
首先创建足够大空间的slice
stack := make([]rune, n_enough)
然后设置一个头部指针 top := -1
则push操作为
top++
stack[top] = c
则pop操作为
if top >= 0 {
c = stack[top]
top–
}
GO的运行效率真的高。。。约等于 0 ms
python实现
class Solution:
def isValid(self, s: str) -> bool:
hash = {'(':')', '{':'}', '[':']'}
stack = []
for char in s:
char = str(char)
if char == '(' or char == '{' or char == '[':
stack.append(char)
elif char == ')' or char == '}' or char == ']':
if len(stack) == 0:
return False
cpr = stack.pop()
if char != hash.get(cpr):
return False
if len(stack) != 0:
return False
return True
注意,list对象本身就相当于一个栈stack。append方法即是push功能,pop方法就是pop功能。
其次,python的字符串比较可以直接 str1 == str2
Day12 合并两个有序链表
将两个有序链表合并为一个新的有序链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
示例:
输入:1->2->4, 1->3->4
输出:1->1->2->3->4->4
算法思路
递归算法
即函数的本体功能为确定一个节点,而尾部由递归产生
即
func()
node = compare(l1, l2)
rear = func()
node.next = rear
return node
GO实现
func mergeTwoLists(l1 *ListNode, l2 *ListNode) *ListNode {
if l1 == nil {
return l2
} else if l2 == nil {
return l1
}
if l1.Val > l2.Val{
temp := l2
l2 = l1
l1 = temp
}
result := l1
rear := mergeTwoLists(l1.Next,l2)
(*result).Next = rear
return result
}
python实现
class Solution:
def mergeTwoLists(self, l1: ListNode, l2: ListNode) -> ListNode:
if l1 and l2:
if l1.val > l2.val: l1, l2 = l2, l1
l1.next = self.mergeTwoLists(l1.next, l2)
return l1 or l2
注意 return l1 or l2 的用法是:当两者都为真(比如都不为0或None)
则返回左边的 l1,若l1为0或者None,则返回l2
Day13 合并K表
递归实现
class Solution:
def mergeKLists(self, lists: List[ListNode]) -> ListNode:
n = len(lists)
best = None
best_id = 0
for i in range(0,n):
if lists[i] == None:
continue
if best == None or lists[i].val <= best:
best = lists[i].val
best_id = i
if best == None:
return None
result = lists[best_id]
lists[best_id] = lists[best_id].next
result.next = self.mergeKLists(lists)
return result
Tips:
最大值最小值的初始化方法
best = None
if best == None or k[i] > best :
best = k[i]
但是运行时间超时,递归不是个好办法,将原问题转换为排序问题
即将链表转为数组,然后排序,再转链表
Go实现
import "sort"
func mergeKLists(lists []*ListNode) *ListNode {
biglist := make([]int, 0)
for K := 0; K < len(lists); K++ {
p := lists[K]
for p != nil {
biglist = append(biglist, p.Val)
p = p.Next
}
}
sort.Slice(biglist, func(a, b int) bool {
if biglist[a] < biglist[b] {
return true
}
return false
})
p := &ListNode{0, nil}
result := p
for i := 0; i < len(biglist); i++ {
p.Next = &ListNode{Val: biglist[i], Next: nil}
p = p.Next
}
return result.Next
}
tips:
像上文 next是一个指针,则p.Next = &ListNode{xxx} 注意使用 &
python 实现
class Solution:
def mergeKLists(self, lists: List[ListNode]) -> ListNode:
n = len(lists)
biglist = []
for i in range(0,n):
p = lists[i]
while p != None:
biglist.append(p.val)
p = p.next
biglist.sort()
p = ListNode(0)
result = p
for id in range(0,len(biglist)):
p.next = ListNode(biglist[id])
p = p.next
return result.next
Day14 删除排序数组中的重复项
给定一个排序数组,你需要在原地删除重复出现的元素,使得每个元素只出现一次,返回移除后数组的新长度。
不要使用额外的数组空间,你必须在原地修改输入数组并在使用 O(1) 额外空间的条件下完成。
示例 1:
给定数组 nums = [1,1,2],
函数应该返回新的长度 2, 并且原数组 nums 的前两个元素被修改为 1, 2。
你不需要考虑数组中超出新长度后面的元素。
示例 2:
给定 nums = [0,0,1,1,1,2,2,3,3,4],
函数应该返回新的长度 5, 并且原数组 nums 的前五个元素被修改为 0, 1, 2, 3, 4。
你不需要考虑数组中超出新长度后面的元素。
算法点评
其实就是双索引的办法,一个用于索引写入位置 j,一个用于索引待判断是否重复的元素 i
GO实现
func removeDuplicates(nums []int) int {
n := len(nums)
j := 1
if n < 2 {
return n
}
for i := 1; i < n; i++ {
if nums[i] != nums[i-1] {
nums[j] = nums[i]
j++
}
}
return j
}
tips
记得判断临界条件 n < 2时的情况
Python实现
class Solution:
def removeDuplicates(self, nums: List[int]) -> int:
n = len(nums)
if n < 2:
return n
j = 1
for i in range(1,n):
if nums[i] != nums[i-1]:
nums[j] = nums[i]
j += 1
return j
Day15 搜索旋转排序数组
使用二分思想进行查找
class Solution:
def search(self, nums, target: int) -> int:
n = len(nums)
if n <= 0:
return 0
left = 0
right = n-1
mid = (left + right) // 2
if target >= nums[0]:#在左边
while right - left > 1:
if nums[mid] == target:
return mid
elif nums[mid] < nums[0]:#在右区
right_temp = mid
mid_temp = (right_temp + left) // 2
if nums[mid_temp] >= nums[0] and nums[mid_temp] < target:#在左区 且在target左边
left = mid
mid = (left + right) // 2
else:
right = right_temp
mid = mid_temp
else:
while right - left > 1:
if nums[mid] == target:
return mid
elif nums[mid] >= nums[0]:#在左区
left_temp = mid
mid_temp = (left_temp + right) // 2
if nums[mid_temp] < nums[0] and nums[mid_temp] > target:#在右区 且在target左边
right = mid
mid = (left + right) // 2
else:
left = left_temp
mid = mid_temp
if nums[left] == target:
return left
else:
return right
超时
别人的设计思想几乎与我的一致
代码如下
class Solution:
def search(self, nums: List[int], target: int) -> int:
def half_search(nums, target, i, j, head):
mid = int(0.5 * (j + i))
if i > j:
return -1
if nums[mid] == target:
return mid
if (nums[mid] < target < head) or (head <= nums[mid] < target) or (nums[mid] >= head and target < head):
return half_search(nums, target, mid + 1, j, head)
else:
return half_search(nums, target, i, mid-1, head)
if not nums:
return -1
head = nums[0]
return half_search(nums, target, 0, len(nums) - 1, head)
设计要点
使用递归进行二分
××初始化要保证left 和 right 不是target,若是直接返回
××计算得到mid要判断mid 是不是target,如果不是,left = mid 或者 right = mid 这样可以永远保证 left 和 right 和 mid 不等于 target
××不要忘记强制退出的条件,以防target不存在于nums中。
设计哲学:
1.代码要简单到显然没有错误
2.能循环的不递归
优化后的代码
GO实现
func search(nums []int, target int) int {
n := len(nums)
if n <= 0 {
return -1
}
right := n - 1
left := 0
head := nums[0]
if nums[left] == target {
return left
} else if nums[right] == target {
return right
}
for right-left > 1 {
mid := (right + left) / 2
if nums[mid] == target {
return mid
}
if (target > nums[mid] && nums[mid] >= head) || (head > target && target > nums[mid]) || (target < head && nums[mid] >= head) {
left = mid
} else {
right = mid
}
}
return -1
}
class Solution:
def search(self, nums: List[int], target: int) -> int:
if len(nums) <= 0:
return -1
i = 0
j = len(nums) - 1
if nums[i] == target:
return i
elif nums[j] == target:
return j
while j - i > 1:
mid = (j + i) // 2
if nums[mid] == target:
return mid
if (nums[mid] < target < nums[0]) or (nums[0] <= nums[mid] < target) or (nums[mid] >= nums[0] and target < nums[0]):
i = mid
else:
j = mid
return -1
Day16 字符串相乘
简单思路是字符串逐字符乘法,然后使用数组进行累加和进位
进位即是使用一个简单技巧
num_temp[f] += mul1 * mul2
num_temp[f+1] += num_temp[f] / 10
num_temp[f] %= 10
Go实现
func multiply(num1 string, num2 string) string {
n := len(num1)
num_final := make([]int, 1)
for i := 0; i < n; i++ {
pow := n - 1 - i
// 乘法器
mul1 := int(num1[i] - '0')
num_temp := make([]int, pow+len(num2)+1)
f := pow
for j := len(num2) - 1; j >= 0; j-- {
mul2 := int(num2[j] - '0')
num_temp[f] += mul1 * mul2
num_temp[f+1] += num_temp[f] / 10
num_temp[f] %= 10
f++
}
for q := 0; q < len(num_temp); q++ {
if q >= len(num_final)-1 {
num_final = append(num_final, 0)
}
num_final[q] += num_temp[q]
num_final[q+1] += num_final[q] / 10
num_final[q] %= 10
}
}
for q := 0; q < len(num_final)-1; q++ {
num_final[q+1] += num_final[q] / 10
num_final[q] %= 10
}
result := ""
for i := len(num_final) - 1; i >= 0; i-- {
if result == "" && num_final[i] == 0 {
continue
}
result = result + string(num_final[i]+'0')
}
if result == "" {
return "0"
}
return result
}
tips:
注意,string(int_a + ‘0’) 记得 加上 ‘0’
Day17 全排列
使用递归结合动态规划思想实现全排列
即 从这堆数中,抽取一个数A,作为组合排列的头(或者尾也可以,一样的效果)
剩余的数按同样的办法排列,并返回一个排列好的列表回来,给列表的头部加上A得到完整的排列
Python实现
class Solution:
def permute(self, nums:list):
if len(nums) == 1:
return [nums]
all_list = []
for a in nums:
num_cpy = nums.copy()
num_cpy.remove(a)
nlist = self.permute(num_cpy)
for l in nlist:
l.append(a)
all_list.append(l)
return all_list
实现效果还不错
一个不优秀的解法:
回朔算法:
思路是先swap调换nums的排列,然后加入result
再然后恢复为原来的nums排列
回朔应与递归结合使用
Go实现
func backtrace(nums []int, res [][]int, i int) [][]int {
if i == len(nums) {
tmp2 := make([]int, len(nums))
copy(tmp2, nums)
res := append(res, tmp2)
return res
}
for j := i; j < len(nums); j++ {
temp := nums[i]
nums[i] = nums[j]
nums[j] = temp
res = backtrace(nums, res, i+1)
nums[j] = nums[i]
nums[i] = temp
}
return res
}
func permute(nums []int) [][]int {
result := make([][]int, 0)
return backtrace(nums, result, 0)
}
tips
如果有nums是 []int 类型,result是 [][]int类型,那么nums和result这个变量名是一个引用,那么 result = append(result, nums) 会把nums的引用添加到result中,一旦nums的内容发生变化,result的内容也会变化!!!因此要先把nums拷贝一份nums_cpy,然后把nums_cpy放入到result中才是正确做法。
`
在GO中,有三种引用类型,slice,map,struct。因此比如有一个变量result [][]int,那么有 result = append(result, nums),执行完这条语句,执行前和执行后的result不是同一个!引用的地址是不一样的。而result只是一个引用变量,可以随时替换成别的slice。。因此,如果要保存result的修改结果,要修改完后,return result 返回回去, 比如上述代码第6行。
Day18 最大自序和
给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。
示例:
输入: [-2,1,-3,4,-1,2,1,-5,4],
输出: 6
解释: 连续子数组 [4,-1,2,1] 的和最大,为 6。
GO实现
func maxSubArray(nums []int) int {
n := len(nums)
sum := -1
best := 0
first := true
for i := 0; i < n; i++ {
if sum+nums[i] > nums[i] {
sum = sum + nums[i]
} else {
sum = nums[i]
}
if sum > best || first {
best = sum
first = false
}
}
return best
}
Day19 螺旋矩阵
题目
GO实现
func spiralOrder(matrix [][]int) []int {
max_i := len(matrix)
if max_i <= 0{
return make([]int,0)
}
min_i := -1
min_j := -1
max_j := len(matrix[0])
i := 0
j := 0
dir_ver := 0
dir_hor := 1
result := make([]int, 0)
for i > min_i && i < max_i && j > min_j && j < max_j {
result = append(result, matrix[i][j])
if dir_hor > 0 && j+dir_hor >= max_j {
dir_hor = 0
dir_ver = 1
min_i++
} else if dir_hor < 0 && j+dir_hor <= min_j {
dir_hor = 0
dir_ver = -1
max_i--
} else if dir_ver < 0 && i+dir_ver <= min_i {
dir_hor = 1
dir_ver = 0
min_j++
} else if dir_ver > 0 && i+dir_ver >= max_i {
dir_hor = -1
dir_ver = 0
max_j--
}
i += dir_ver
j += dir_hor
}
return result
}
tips
要避免访问已经访问过的,除了设置visited[] 数组的方式,也可以通过不断移动边界来限制访问。边界移动法
Day20 螺旋矩阵II
Python 实现
class Solution:
def generateMatrix(self, n: int) -> List[List[int]]:
matrix = [[0 for j in range(0,n)] for i in range(0,n)]
max_i = n
max_j = n
min_i = -1
min_j = -1
k = 1
i = 0
j = 0
dir_j = 1
dir_i = 0
while i > min_i and i < max_i and j > min_j and j < max_j:
matrix[i][j] = k
k += 1
if dir_j > 0 and dir_j + j >= max_j:
dir_j = 0
dir_i = 1
min_i += 1
elif dir_j < 0 and dir_j + j <= min_j:
dir_j = 0
dir_i = -1
max_i -= 1
elif dir_i > 0 and dir_i + i >= max_i:
dir_i = 0
dir_j = -1
max_j -= 1
elif dir_i < 0 and dir_i + i <= min_i:
dir_i = 0
dir_j = 1
min_j += 1
i += dir_i
j += dir_j
return matrix
Day21 旋转链表
思路
先将链表首尾相接,再断链条
GO实现
/**
* Definition for singly-linked list.
* type ListNode struct {
* Val int
* Next *ListNode
* }
*/
func rotateRight(head *ListNode, k int) *ListNode {
p := head
if p == nil{
return nil
}
n := 1
for p.Next != nil{
p = p.Next
n++
}
p.Next = head
for i := n - (k % n); i>0;i--{
p = p.Next
}
result := p.Next
p.Next = nil
return result
}
Day22 不同路径
很简单,其实就是求解 C(m+n-2. m-1)
Python实现
class Solution:
def uniquePaths(self, m: int, n: int) -> int:
if m < 1 or n < 1:
return 0
sum_up = 1
for i in range(1,m+n-1):
sum_up *= i
sum_m = 1
for i in range(1,m):
sum_m *= i
sum_n = 1
for i in range(1,n):
sum_n *= i
return int(sum_up / (sum_m*sum_n))
tips:
再次强调注意,i in range() 是闭开区间的,左闭右开
Day23 爬楼梯
Python实现
class Solution:
def climbStairs(self, n: int) -> int:
max_d = n // 2
total = 0
for i in range(0,max_d+1):
M = i + n - 2*i
N = M - i if M - i > i else i
sum_up = 1
for j in range(N+1, M+1):
sum_up *= j
sum_do = 1
for j in range(1, M-N+1):
sum_do *= j
total += int(sum_up/sum_do)
return total
GO实现
func climbStairs(n int) int64 {
max_double := n / 2
var total int64
total = 0
for i := 0; i <= max_double; i++ {
M := i + n - 2*i
N := 0
if M-i > i {
N = M - i
} else {
N = i
}
var sum_up int64
sum_up = 1
for j := N + 1; j <= M; j++ {
sum_up *= int64(j)
}
var sum_d int64
sum_d = 1
for j := 1; j <= M-N; j++ {
sum_d *= int64(j)
}
total += (sum_up / sum_d)
}
return total
}
明明和python是完全一样的实现,为什么go的这段代码,当 n = 44时 得到的解是错误的???
原因:
Golang中第17行运算数字过大,导致越界。。而python默认含有大数类型。。
果然还是python适合做leetcode
更好的办法:
动态规划法
思想:
到达第 i 阶楼梯的办法有且仅有两个:1.在第 i-1 阶楼梯上,走一步即到达 i ,或者在 i-2 阶楼梯上走两步到达 i,因此第 i 阶楼梯的可能性总数dp[i]等于 dp[i-1] + dp[i-2]
func climbStairs(n int) int {
if n < 2 {
return n
}
dp := make([]int, n+1)
dp[1] = 1
dp[2] = 2
for i := 3; i <= n; i++ {
dp[i] = dp[i-1] + dp[i-2]
}
return dp[n]
}
太棒的做法了!多学动态规划
Day24 子集
其实这道题就相当于自己实现组合
组合的实现思想:
比如
C(nums,n):
nums数组中,pop出一个a,剩下的元素进行组合 lists = C(nums, n-1)
然后把a加入到lists中的每一个数组进去。一直pop直到nums的长度小于n
返回条件是:n等于1时,将nums打包成组合列表返回去
Golang实现
func subsets(nums []int) [][]int {
if len(nums) == 0 {
return make([][]int, 0)
}
result := append(make([][]int, 0), []int{})
for i := 1; i <= len(nums); i++ {
result = append(result, C(nums, i)...)
}
return result
}
func C(nums []int, n int) [][]int {
result := make([][]int, 0)
if n == 1 {
for _, item := range nums {
result = append(result, []int{item})
}
return result
}
for len(nums) >= n {
a := nums[0]
nums = append([]int{}, nums[1:]...)
lists := C(nums, n-1)
for j, item := range lists {
lists[j] = append(item, a)
}
result = append(result, lists...)
}
return result
}
Python实现
class Solution:
def subsets(self, nums: List[int]) -> List[List[int]]:
result = [[]]
if len(nums) == 0:
return result
for i in range(1, len(nums)+1):
lists = self.C(nums, i)
for item in lists:
result.append(item)
return result
def C(self, nums: List[int], n:int) -> List[List[int]]:
result = []
if n == 1:
for item in nums:
result.append([item])
return result
while len(nums) >= n:
nums = nums.copy()
a = nums.pop()
lists = self.C(nums,n-1)
for item in lists:
item.append(a)
result.append(item)
return result
注意:这里的要有 nums = nums.copy()
不然 a = nums.pop() 这个语句会破坏原来的数组
Day25 合并两个有序表
Go实现
func merge(nums1 []int, m int, nums2 []int, n int) {
nums1_cpy := make([]int ,len(nums1))
copy(nums1_cpy, nums1)
q := 0
t := 0
for i :=0;i<m+n;i++{
if (t < n && nums1_cpy[q] >= nums2[t]) || q >= m{
nums1[i] = nums2[t]
t ++
}else{
nums1[i] = nums1_cpy[q]
q++
}
}
}
Day26 格雷编码
发现其特点
上图来源(https://blog.csdn.net/u012501459/article/details/46790683)
存在镜像关系
因此使用递归动态规划方式来实现
Go实现
func grayCode(n int) []int {
if n == 0 {
return []int{0}
} else if n == 1 {
return []int{0, 1}
}
list := grayCode(n - 1)
list2 := make([]int, 0)
pow := 1
for i := 0; i < n-1; i++ {
pow *= 2
}
for i := len(list) - 1; i >= 0; i-- {
list2 = append(list2, list[i]+pow)
}
list = append(list, list2...)
return list
}
Python实现
class Solution:
def grayCode(self, n: int) -> List[int]:
if n == 0:
return [0]
if n == 1:
return [0,1]
lists = self.grayCode(n-1)
lists_rev = []
for i in range(len(lists)-1, -1,-1):
lists_rev.append(2**(n-1) + lists[i])
return lists + lists_rev
小tips:
再python中,连接两个list可以直接 list = list1+list2 即可连接
再python中,类调用自身方法要 self.functionname()
Day27 二叉树的最大深度
递归实现
Golang
func maxDepth(root *TreeNode) int {
if root == nil {
return 0
}
return depth(root, 0)
}
func depth(node *TreeNode, h int) int {
if node == nil {
return h
}
h++
if node.Left == nil && node.Right == nil {
return h
}
h1 := depth(node.Left, h)
h2 := depth(node.Right, h)
if h1 > h2 {
return h1
} else {
return h2
}
}
Python实现
class Solution:
def maxDepth(self, root: TreeNode) -> int:
if root == None:
return 0
return self.depth(root, 0)
def depth(self, node:TreeNode, h:int):
if node == None:
return h
h = h+1
if node.left == None and node.right == None:
return h
h1 = self.depth(node.left, h)
h2 = self.depth(node.right, h)
return max(h1,h2)
Day28 买卖股票的最佳时机
Go实现
func maxProfit(prices []int) int {
min := -1
best := 0
n := len(prices)
for i := 0; i < n; i++ {
if min == -1 || prices[i] < min {
min = prices[i]
}
delta := prices[i] - min
if delta > best {
best = delta
}
}
return best
}
时间复杂度 O(n)
Day29 买卖股票的最佳时机 II
思路很简单,不断进行峰减谷的操作就行了
Golang实现
func maxProfit(prices []int) int {
profit := 0
status := 2
temp := 0
n := len(prices)
for i := 0; i<n; i++{
if status != 1 && (i+1>=n || prices[i+1] > prices[i]) {// 谷
status = 1
temp = -prices[i]
}else if status != 2 && (i+1>=n || prices[i+1] < prices[i]) {// 峰
status = 2
profit = profit + temp + prices[i]
temp = 0
}
}
return profit
}
Day30 二叉树中的最大路径和
Golang实现
var maxx int
func max(nums []int) int {
best := nums[0]
for _, item := range nums {
if item > best {
best = item
}
}
return best
}
func maxPathSum2(root *TreeNode) int {
if root == nil {
return 0
}
l1 := maxPathSum2(root.Left)
l2 := maxPathSum2(root.Right)
bestTree := max([]int{root.Val + l1, root.Val + l2, 0})
maxx = max([]int{maxx, l1 + l2 + root.Val})
return bestTree
}
func maxPathSum(root *TreeNode) int {
maxx = root.Val
maxPathSum2(root)
return maxx
}
tips :
1.Go语言不能再全局区域对变量进行赋值,所以先声明var maxx int 然后再赋值
2.for+range循环时,如果range对象是数组,那么应该是 i, item := range list 第二个才是数值,第一个是下标索引
3.简化问题的一个思路:如果遇到负数,那么就不要加上去,因为负数会让现有数变小
Day31 只出现一次的数字
思路:使用哈希表,把出现过的存起来,再次出现就删掉
func singleNumber(nums []int) int {
hash := make(map[int]int, 0)
for _, item := range nums {
if _, ok := hash[item]; ok {
delete(hash, item)
} else {
hash[item] = item
}
}
for k, _ := range hash {
return k
}
return 0
}
tips:
1.Go中判断一个元素是否在哈希表里的办法是:if _, ok := hash[item]; ok
2.删除哈希表中某个键值对用的是delete方法,delete(hash, key)
Python实现:
class Solution:
def singleNumber(self, nums: List[int]) -> int:
map = dict()
for item in nums:
if item in map.keys():
map.pop(item)
else:
map[item] = item
return map.popitem()[0]
tips:
1.python中,判断元素在dict中的方法是:item in map.keys()
2.python中,哈希表是 map = dict()
3.删除哈希表的一个元素是 map.pop(key)
4.字典弹出一个键值对,map.popitem() 得到一个元组,但取元组的第一个元素。map.popitem()[0]
Day32 环形链表
用哈希来求解
func hasCycle(head *ListNode) bool {
if head == nil{
return false
}
hash := make(map[*ListNode]int)
p := head
for p.Next != nil {
if _, ok := hash[p]; ok{
return true
}
hash[p] = 0
p = p.Next
}
return false
}
快慢指针法
Python实现
class Solution(object):
def hasCycle(self, head):
"""
:type head: ListNode
:rtype: bool
"""
if head == None:
return False
slow = head
fast = head.next
while slow != fast:
if fast == None or fast.next == None:
return False
slow = slow.next
fast = fast.next.next
return True
因为有环,所以快指针总有一天会追上慢指针。
Day33 环形链表 II
Golang实现
func detectCycle(head *ListNode) *ListNode {
if head == nil{
return nil
}
hash := make(map[*ListNode]*ListNode)
p := head
for p.Next != nil {
if result, ok := hash[p]; ok{
return result
}
hash[p] = p
p = p.Next
}
return nil
}
Day34 LRU缓存机制
基本的实现思想:
获取数据,或者判断数据是否存在,使用哈希表来实现
而频繁度则采用链表来调整节点位置
Golang实现
type Node struct {
key int
val int
prev *Node
next *Node
}
type LRUCache struct {
capacity int
length int
hash map[int]*Node
head *Node
rear *Node
}
func Constructor(capacity int) LRUCache {
head := &Node{0, 0, nil, nil}
rear := &Node{0, 0, head, nil}
head.next = rear
return LRUCache{capacity, 0, make(map[int]*Node, 0), head, rear}
}
func (this *LRUCache) MoveToHead(node *Node) {
A := node.prev
B := node.next
A.next = B
B.prev = A
this.InsertToHead(node)
}
func (this *LRUCache) InsertToHead(node *Node) {
this.head.next.prev = node
node.next = this.head.next
node.prev = this.head
this.head.next = node
}
func (this *LRUCache) Get(key int) int {
if node, ok := this.hash[key]; ok {
this.MoveToHead(node)
return node.val
}
return -1
}
func (this *LRUCache) DeleteRear() {
this.rear.prev.prev.next = this.rear
this.rear.prev = this.rear.prev.prev
}
func (this *LRUCache) Put(key int, value int) {
if node, ok := this.hash[key]; ok {
this.MoveToHead(node)
node.val = value
} else if this.length >= this.capacity {
delete(this.hash, this.rear.prev.key)
this.DeleteRear()
node := &Node{key, value, nil, nil}
this.InsertToHead(node)
this.hash[key] = node
} else {
node := &Node{key, value, nil, nil}
this.InsertToHead(node)
this.hash[key] = node
this.length++
}
}
tips:注意事项
链表的边界条件,比如首节点尾节点为nil,这种情况,最好的解决办法是设置边界节点,比如本实现中,提前设定好节点 head 和 rear 都是空数据的节点,并且固定不变,将动态变化的节点插入到这两个固定的节点之间,从而简化编程难度
任何重复的代码都值得独立成一个子函数,通过调用函数的办法去使用
双向链表中节点的改动一定要保证好双向的节点的调整
Day35 排序链表
Golang实现
func sortList(head *ListNode) *ListNode {
if head == nil {
return head
}
p_src := head
p_dst := &ListNode{head.Val, nil}
for p_src != nil {
next := p_src.Next
p := p_dst
insertToRear := true
for p.Next != nil {
if p_src.Val < p.Next.Val{
p_src.Next = p.Next
p.Next = p_src
insertToRear = false
break
}
p = p.Next
}
if insertToRear {
p_src.Next = nil
p.Next = p_src
}
p_src = next
}
return p_dst.Next
}
排序问题的控制空间复杂度的办法是:
利用原空间,不用创建新空间
排序插入的标准办法是:
一一比较,遇到合适的就插入进去并设置insertToRear为假,如果一直没有合适的 insertToRear 为真
Python 实现
class Solution:
def sortList(self, head: ListNode) -> ListNode:
if head == None:
return head
p_src = head
p_dst = ListNode(head.val)
while p_src != None:
nex = p_src.next
insertToRear = True
p = p_dst
while p.next != None:
if p.next.val > p_src.val:
p_src.next = p.next
p.next = p_src
insertToRear = False
break
p = p.next
if insertToRear:
p_src.next = None
p.next = p_src
p_src = nex
return p_dst.next
注意!!!
再python中,一个类的对象A,即是没有Next属性,也可以通过A.Next = 3 给它赋值,使这个对象有这个属性。。。这就很坑,你不能写错单词!它也不会给你报错!
采用更好的排序方法:归并排序
算法简介:
把一个链表分为左右两部分
然后左右两部分排好序后,再合并
图片来源:https://www.cnblogs.com/piperck/p/6030122.html
递归实现的伪代码:
func merge(A,B) {
if A < B
C = A
else
C = B
return C
}
func sort(A,end) {
B = getMiddle(A)
left = sort(A,B)
right = sort(B,end)
return merge(A,B)
}
Golang 实现
func mergeLists(left *ListNode, right *ListNode) *ListNode {
result := &ListNode{0, nil}
p := left
q := right
c := result
for p != nil || q != nil {
if (p != nil && q != nil && p.Val < q.Val) || (p != nil && q == nil) {
c.Next = p
p = p.Next
c = c.Next
} else {
c.Next = q
q = q.Next
c = c.Next
}
}
return result.Next
}
func sortList(head *ListNode) *ListNode {
if head == nil || head.Next == nil {
return head
}
// find the split point
slow := head
fast := head
for fast != nil && fast.Next != nil {
fast = fast.Next.Next
if fast != nil && fast.Next != nil {
slow = slow.Next
}
}
right := sortList(slow.Next)
slow.Next = nil
left := sortList(head)
return mergeLists(left, right)
}
tips
链表找中间位置:
快慢指针:快走2步,慢1步
所以快的路程是慢的2倍
所以当快到达终点,慢到达中点
Day36 最小栈
最小栈的核心思想
使用两个栈,一个用来放数据,另一个用来放当前时刻,最小元素
stack和minstack
一旦pop,两个stack同时pop
这样就可以保证从minstack取出来的元素总是当前时刻最小元素
Golang实现
type MinStack struct {
minstack []int
stack []int
}
func (this *MinStack) Push(x int) {
min := 0
if len(this.minstack) == 0 || this.minstack[len(this.minstack)-1] > x{
min = x
}else{
min = this.minstack[len(this.minstack)-1]
}
this.minstack = append(this.minstack, min)
this.stack = append(this.stack, x)
}
func (this *MinStack) Pop() {
this.stack = this.stack[:len(this.stack)-1]
this.minstack = this.minstack[:len(this.minstack)-1]
}
func (this *MinStack) Top() int {
result := this.stack[len(this.stack)-1]
return result
}
func (this *MinStack) GetMin() int {
result := this.minstack[len(this.minstack)-1]
return result
}
Day37 相交链表
使用哈希表
Golang实现
func getIntersectionNode(headA, headB *ListNode) *ListNode {
hash := make(map[*ListNode]*ListNode, 0)
p := headA
q := headB
i := 1
for p != nil || q != nil {
if i % 2 == 1 && p != nil{
if node, ok := hash[p]; ok {
return node
}
hash[p] = p
p = p.Next
} else if q != nil {
if node, ok := hash[q]; ok {
return node
}
hash[q] = q
q = q.Next
}
i++
}
return nil
}
不过最好是先计算出两个链表的长度,然后长的那条指针移动到和短的平齐,然后各自走一步,判断一步,走一步,判断一步
Day38 求众数
GO实现
func majorityElement(nums []int) int {
stack := make([]int, 0)
for _, item := range nums {
if len(stack) == 0 || stack[len(stack) - 1] == item {
stack = append(stack, item)
} else {
stack = stack[:len(stack) - 1]
}
}
return stack[len(stack) - 1]
}
算法思想:消消乐抵消法,因为众数出现 n/2 次,那么抵消到最后肯定有剩余
Day39 反转链表
Go实现
func reverseList(head *ListNode) *ListNode {
var temp *ListNode
temp = nil
p := head
for p != nil {
next := p.Next
p.Next = temp
temp = p
p = next
}
return temp
}
Day40 数组中的第K个最大元素
思路:
使用堆排序,建立一个小顶堆,小顶堆的长度是k,
首先对数组的前k个元素建立小顶堆
然后对数组中第k个元素后面的元素 i ,一一做判断
若元素i 大于堆顶 nums[0] 则和堆顶交换
然后重新建立堆 确保堆顶元素总是堆中最小
如果小于堆顶,则不作处理,跳过
小顶堆建立的思想:
计算root和child的左边,root是0 ~ (n-1)/2
对于每个root,其child 是 root * 2 + 1和 root * 2 + 2
但是child不一定存在,因此要判断
调整办法:
先比较两个child,小的那个child_s和root做比较,如果小于root,则交换root和child_s的数据,然后root = child_s,进入再次以新的root进行判断,否则break
Golang实现
func findKthLargest(nums []int, k int) int {
heapBuild(nums, k)
n := len(nums)
for i := k - 1; i < n; i++ {
if nums[0] < nums[i] {
nums[0], nums[i] = nums[i], nums[0]
heapBuild(nums, k)
}
}
heapSort(nums, k)
return nums[k-1]
}
func heapBuild(nums []int, k int) {
for root := (k - 1) / 2; root >= 0; root-- {
minheap(root, k, nums)
}
}
func heapSort(nums []int, k int) {
for end := k - 1; end >= 0; end-- {
if nums[0] < nums[end] {
nums[0], nums[end] = nums[end], nums[0]
minheap(0, end-1, nums)
}
}
}
func minheap(root int, end int, nums []int) {
for {
child := root*2 + 1
if child >= end {
break
}
if child+1 < end && nums[child+1] < nums[child] { // 是小于 不是小于等于
child = child + 1
}
if nums[child] < nums[root] {
nums[root], nums[child] = nums[child], nums[root]
root = child
} else {
break
}
}
}
Day41 存在重复元素
Python 实现
class Solution:
def containsDuplicate(self, nums: List[int]) -> bool:
map = dict()
for item in nums:
if item in map.keys():
return True
map[item] = item
return False
Day42 二叉搜索树
使用迭代(栈)+中序遍历
Go实现
func kthSmallest(root *TreeNode, k int) int {
stack := make([]*TreeNode, 0)
stack = append(stack, root)
i := 0
for len(stack) != 0 {
node := stack[len(stack) - 1]
stack = stack[:len(stack) - 1]
if node.Right == nil && node.Left == nil{
if i++; i == k{
return node.Val
}
continue
}
if node.Right != nil {
stack = append(stack, node.Right)
}
stack = append(stack, node)
if node.Left != nil {
stack = append(stack, node.Left)
}
node.Left = nil
node.Right = nil
}
return root.Val
}
Day43 2的幂
Golang实现
func isPowerOfTwo(n int) bool {
if n <= 0 {
return false
}
if n&(n-1) == 0 {
return true
}
return false
}
Python实现
class Solution:
def isPowerOfTwo(self, n: int) -> bool:
if n <= 0:
return False
if (n&(n-1)) == 0:
return True
return False
Day44 二叉搜索树的最近公共祖先
直接使用深度优先搜索算法:
Golang实现
func search(root, p, q *TreeNode) (bool, *TreeNode) {
if root == nil {
return false, nil
}
found_left, n1 := search(root.Left, p, q)
found_right, n2 := search(root.Right, p, q)
found_root := false
if n1 != nil {
return true, n1
}
if n2 != nil {
return true, n2
}
if p == root {
found_root = true
}
if q == root {
found_root = true
}
if (found_left && found_right) || (found_root && (found_left || found_right)) {
return true, root
}
return (found_left || found_right || found_root), nil
}
func lowestCommonAncestor(root, p, q *TreeNode) *TreeNode {
_, result := search(root, p, q)
return result
}
更好的做法是:
因为这是二叉搜索树,所以两个节点的祖先的值肯定介于这两个节点的值之间,利用这个原理:
Python实现
class Solution:
def lowestCommonAncestor(self, root: 'TreeNode', p: 'TreeNode', q: 'TreeNode') -> 'TreeNode':
queue = []
queue.append(root)
while len(queue) != 0 :
item = queue[0]
queue.remove(item)
if ( p.val <= item.val and item.val <= q.val) or ( q.val <= item.val and item.val <= p.val):
return item
if item.left != None:
queue.append(item.left)
if item.right != None:
queue.append(item.right)
Day45 二叉树的最近公共祖先
使用深度优先搜索递归的办法
Python实现
class Solution:
def depth(self, root: 'TreeNode', p: 'TreeNode', q: 'TreeNode') -> ('TreeNode', 'bool'):
if root == None:
return None, False
found_root = False
n1, found_left = self.depth(root.left,p,q)
n2, found_right = self.depth(root.right,p,q)
if n1 != None:
return n1, True
if n2 != None:
return n2, True
if root == p or root == q :
found_root = True
if (found_root and (found_left or found_right)) or (found_left and found_right):
return root, True
elif found_root or found_left or found_right:
return None, True
return None, False
def lowestCommonAncestor(self, root: 'TreeNode', p: 'TreeNode', q: 'TreeNode') -> 'TreeNode':
node, q = self.depth(root,p,q)
return node
Python的类函数,返回双返回值时
def function(self, root :'TreeNode') -> ('TreeNode', 'bool')
使用括号包其双返回值的类型
Day46 删除链表中的节点
很简单,不解释
Python实现
class Solution:
def deleteNode(self, node):
"""
:type node: ListNode
:rtype: void Do not return anything, modify node in-place instead.
"""
if node == None:
return node
p = node
prev = p
while p.next:
p.val = p.next.val
prev = p
p = p.next
prev.next = None
Day47 除自身以外数组的乘积
动态规划法:
output每一个元素等于nums中,该元素的左部分乘积乘以有部分乘积
基于这个思想,算法为:
class Solution:
def productExceptSelf(self, nums: List[int]) -> List[int]:
if len(nums) <= 1:
return nums
dp = [1 for i in range(0,len(nums))]
result = [1 for i in range(0,len(nums))]
dp[0] = 1
result[len(nums)-1] = 1
for i in range(len(nums)-2, -1, -1):
result[i] = result[i+1] * nums[i+1]
for i in range(1,len(nums)):
dp[i] = dp[i-1] * nums[i-1]
result[i] *= dp[i]
result[0] *= dp[0]
return result
GO实现
func productExceptSelf(nums []int) []int {
if len(nums) <= 1 {
return nums
}
dp1 := make([]int, len(nums))
result := make([]int, len(nums))
dp1[0] = 1
result[len(nums)-1] = 1
for i:=len(nums)-2; i >= 0; i-- {
result[i] = result[i+1] * nums[i+1]
}
for i:=1; i<len(nums) ; i++{
dp1[i] = dp1[i-1] * nums[i-1]
result[i] *= dp1[i]
}
result[0] *= dp1[0]
return result
}
但是GO实现会超时。。应该是题目过分苛刻了。
Day48 Nim 游戏
思路:
若n可以被4整除,则b赢,否则自己a赢
Golang实现
func canWinNim(n int) bool {
if n % 4 == 0 {
return false
}
return true
}
tips
这题是考察算法思维的
首先应该尽可能多地得到结果,观察规律,然后用简单取巧的办法实现。
Day49 反转字符串
GO实现
func reverseString(s []byte) {
if len(s) <= 1 {
return
}
j := len(s) - 1
i := 0
for i < j {
tmp := s[j]
s[j] = s[i]
s[i] = tmp
i++
j--
}
}
Day50 反转字符串中的单词 III
Go实现
func reverseWords(s string) string {
if len(s) <= 1 {
return s
}
result := ""
tmp := ""
for i := 0; i<len(s); i++{
if s[i] == ' ' {
result += tmp
result += string(' ')
tmp = ""
continue
}
tmp = string(s[i]) + tmp
}
result += tmp
return result
}
Leetcode139 wordbreak
一个糟糕的递归实现方法:
Golang
func wordBreak(s string, wordDict []string) bool {
front := ""
for i := 0; i < len(s); i++ {
front += string(s[i])
judge := false
for j := 0; j < len(wordDict); j++ {
if front == wordDict[j] {
judge = true
break
}
}
if judge {
if i == len(s)-1 {
return true
}
rear := s[i+1:]
result := wordBreak(rear, wordDict)
if result {
return result
}
}
}
return false
}
虽然可以解,但是可能会遇到超时。