LeetCode 刷题集

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 + Bi

fCarry = 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

值得一提的是,

  1. 再python中, /法并不是整除,因此要用 //
  2. 在尾部替换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

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= 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

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= 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。
LeetCode 刷题集_第1张图片

算法:
双指针法

我们在由线段长度构成的数组中使用两个指针,一个放在开始,一个置于末尾。 此外,我们会使用变量 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]){
            n = len(strs[i])
        }else{
            n = len(cpr)
        }
        for j :=0;j

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–
}
LeetCode 刷题集_第2张图片
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

实现效果还不错
LeetCode 刷题集_第3张图片
一个不优秀的解法:
回朔算法:
思路是先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 螺旋矩阵

题目
LeetCode 刷题集_第4张图片

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[] 数组的方式,也可以通过不断移动边界来限制访问。边界移动法

LeetCode 刷题集_第5张图片

Day20 螺旋矩阵II

LeetCode 刷题集_第6张图片

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 旋转链表

LeetCode 刷题集_第7张图片
思路
先将链表首尾相接,再断链条

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
}

LeetCode 刷题集_第8张图片

Day22 不同路径

LeetCode 刷题集_第9张图片
LeetCode 刷题集_第10张图片

很简单,其实就是求解 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() 是闭开区间的,左闭右开

LeetCode 刷题集_第11张图片

Day23 爬楼梯

LeetCode 刷题集_第12张图片

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

LeetCode 刷题集_第13张图片

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 子集

LeetCode 刷题集_第14张图片
其实这道题就相当于自己实现组合
组合的实现思想:
比如
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 合并两个有序表

LeetCode 刷题集_第15张图片

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= nums2[t]) || q >= m{
            nums1[i] = nums2[t]
            t ++
        }else{
            nums1[i] = nums1_cpy[q]
            q++
        }
    }
}

Day26 格雷编码

LeetCode 刷题集_第16张图片

发现其特点
LeetCode 刷题集_第17张图片
上图来源(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()
LeetCode 刷题集_第18张图片

Day27 二叉树的最大深度

LeetCode 刷题集_第19张图片

递归实现

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 买卖股票的最佳时机

LeetCode 刷题集_第20张图片

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)
LeetCode 刷题集_第21张图片

Day29 买卖股票的最佳时机 II

LeetCode 刷题集_第22张图片LeetCode 刷题集_第23张图片
思路很简单,不断进行峰减谷的操作就行了
Golang实现

func maxProfit(prices []int) int {
    profit := 0
    status := 2
    temp := 0
    n := len(prices)
    for i := 0; i=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 二叉树中的最大路径和

LeetCode 刷题集_第24张图片
LeetCode 刷题集_第25张图片

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
}

LeetCode 刷题集_第26张图片
tips :
1.Go语言不能再全局区域对变量进行赋值,所以先声明var maxx int 然后再赋值
2.for+range循环时,如果range对象是数组,那么应该是 i, item := range list 第二个才是数值,第一个是下标索引
3.简化问题的一个思路:如果遇到负数,那么就不要加上去,因为负数会让现有数变小

Day31 只出现一次的数字

LeetCode 刷题集_第27张图片
思路:使用哈希表,把出现过的存起来,再次出现就删掉

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 环形链表

LeetCode 刷题集_第28张图片
用哈希来求解

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
}

LeetCode 刷题集_第29张图片
快慢指针法
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

LeetCode 刷题集_第30张图片
LeetCode 刷题集_第31张图片

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
}

LeetCode 刷题集_第32张图片

Day34 LRU缓存机制

LeetCode 刷题集_第33张图片
LeetCode 刷题集_第34张图片

基本的实现思想:
获取数据,或者判断数据是否存在,使用哈希表来实现
而频繁度则采用链表来调整节点位置

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 排序链表

LeetCode 刷题集_第35张图片
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 给它赋值,使这个对象有这个属性。。。这就很坑,你不能写错单词!它也不会给你报错!

采用更好的排序方法:归并排序
算法简介:
把一个链表分为左右两部分
然后左右两部分排好序后,再合并
LeetCode 刷题集_第36张图片
图片来源: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 最小栈

LeetCode 刷题集_第37张图片
最小栈的核心思想
使用两个栈,一个用来放数据,另一个用来放当前时刻,最小元素
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 相交链表

LeetCode 刷题集_第38张图片
LeetCode 刷题集_第39张图片

使用哈希表
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 求众数

LeetCode 刷题集_第40张图片
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 反转链表

LeetCode 刷题集_第41张图片
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个最大元素

LeetCode 刷题集_第42张图片

思路:
使用堆排序,建立一个小顶堆,小顶堆的长度是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 存在重复元素

LeetCode 刷题集_第43张图片
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 二叉搜索树

LeetCode 刷题集_第44张图片
LeetCode 刷题集_第45张图片
使用迭代(栈)+中序遍历
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的幂

LeetCode 刷题集_第46张图片
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 二叉搜索树的最近公共祖先

LeetCode 刷题集_第47张图片
LeetCode 刷题集_第48张图片

直接使用深度优先搜索算法:
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 二叉树的最近公共祖先

LeetCode 刷题集_第49张图片
LeetCode 刷题集_第50张图片

使用深度优先搜索递归的办法
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 删除链表中的节点

LeetCode 刷题集_第51张图片
LeetCode 刷题集_第52张图片
很简单,不解释
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 除自身以外数组的乘积

LeetCode 刷题集_第53张图片

动态规划法:
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

但是GO实现会超时。。应该是题目过分苛刻了。

Day48 Nim 游戏

LeetCode 刷题集_第54张图片

思路:
若n可以被4整除,则b赢,否则自己a赢
Golang实现

func canWinNim(n int) bool {
    if n % 4 == 0 {
        return false
    }
    return true
}

tips
这题是考察算法思维的
首先应该尽可能多地得到结果,观察规律,然后用简单取巧的办法实现。

Day49 反转字符串

LeetCode 刷题集_第55张图片
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

LeetCode 刷题集_第56张图片

Go实现

func reverseWords(s string) string {
    if len(s) <= 1 {
        return s
    }
    result := ""
    tmp := ""
    for i := 0; i

Leetcode139 wordbreak

LeetCode 刷题集_第57张图片

一个糟糕的递归实现方法:
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
}

虽然可以解,但是可能会遇到超时。

你可能感兴趣的:(LeetCode 刷题集)