剑指offer(专项突破版)-go语言实现,适合Java转go程序员参考
文章目录
- 2、数组
- 2.2 双指针
- 剑指 Offer II 006. 排序数组中两个数字之和
- 剑指 Offer II 007. 数组中和为 0 的三个数
- 剑指 Offer II 008. 和大于等于 target 的最短子数组
- 剑指 Offer II 009. 乘积小于 K 的子数组
- 2.3 累加数组数字求子数组之和
- 剑指 Offer II 010. 和为 k 的子数组
- 剑指 Offer II 011. 0 和 1 个数相同的子数组
- 剑指 Offer II 012. 左右两边子数组的和相等
- 剑指 Offer II 013. 二维子矩阵的和
- 3、字符串
- 3.2 双指针
- 剑指 Offer II 014. 字符串中的变位词
- 3.3 回文字符串
- 剑指 Offer II 018. 有效的回文
- 剑指 Offer II 019. 最多删除一个字符得到回文
- 剑指 Offer II 020. 回文子字符串的个数
- 4、链表
- 4.3 双指针
- 剑指 Offer II 021. 删除链表的倒数第 n 个结点
- 剑指 Offer II 022. 链表中环的入口节点
- 剑指 Offer II 023. 两个链表的第一个重合节点
- 4.4 反转链表
- 剑指 Offer II 024. 反转链表
- 剑指 Offer II 025. 链表中的两数相加
- 剑指 Offer II 026. 重排链表
- 剑指 Offer II 027. 回文链表
- 4.5 双向链表和循环链表
- 剑指 Offer II 028. 展平多级双向链表
- 剑指 Offer II 029. 排序的循环链表
- 5、哈希表
- 5.2 哈希表的设计
- 剑指 Offer II 030. 插入、删除和随机访问都是 O(1) 的容器
- 剑指 Offer II 031. 最近最少使用缓存
- 5.3 哈希表的应用
- 剑指 Offer II 032. 有效的变位词
- 剑指 Offer II 034. 外星语言是否排序
- 7、队列
- 7.1 基础知识
- 7.2 队列的应用
- 剑指 Offer II 041. 滑动窗口的平均值
- 剑指 Offer II 042. 最近请求次数
- 8、树
- 8.3 二叉搜索树
- 剑指 Offer II 052. 展平二叉搜索树
- 11、二分查找
- 11.2 在排序树组中二分查找
- 剑指 Offer II 068. 查找插入位置
- 剑指 Offer II 069. 山峰数组的顶部
- 11.3 在数值范围内二分查找
- 剑指 Offer II 072. 求平方根
- 12、排序
- 12.2 计数排序
- 剑指 Offer II 075. 数组相对排序
- 总结
2、数组
2.2 双指针
剑指 Offer II 006. 排序数组中两个数字之和
func twoSum(numbers []int, target int) []int {
left := 0
right := len(numbers)-1
for left < right && numbers[left] + numbers[right] != target {
if numbers[left] + numbers[right] < target{
left++
}else {
right--
}
}
return []int{left, right}
}
注意:数组的长度是数组类型的一部分,所以[3]int和[4]int是两种不同的数组类型。所以本题中[]int{left, right}中[]里不能加数字,否则会报错,例如[2]int 无法匹配[]int,所以该如何初始化一个int数组呢?如下所示。
//全局:
var arr0 [5]int = [5]int{1, 2, 3}
var arr1 = [5]int{1, 2, 3, 4, 5}
var arr2 = [...]int{1, 2, 3, 4, 5, 6}
var str = [5]string{3: "hello world", 4: "tom"}
//局部:
a := [3]int{1, 2} // 未初始化元素值为 0。
b := [...]int{1, 2, 3, 4} // 通过初始化值确定数组长度。
c := [5]int{2: 100, 4: 200} // 使用索引号初始化元素。
d := [...]struct {
name string
age uint8
}{
{"user1", 10}, // 可省略元素类型。
{"user2", 20}, // 别忘了最后一行的逗号。
}
剑指 Offer II 007. 数组中和为 0 的三个数
func threeSum(nums []int) [][]int {
res := [][]int{}
if len(nums) >= 3 {
sort.Ints(nums)
i := 0
for i < len(nums) - 2{
res = twoSum(nums, i, res)
temp := nums[i]
for i < len(nums) && nums[i] == temp{
i++
}
}
}
return res
}
func twoSum(nums []int, i int, res [][]int) [][]int {
j := i+1
k := len(nums)-1
for j < k{
if nums[i] + nums[j] + nums[k] == 0{
res = append(res, []int{nums[i], nums[j], nums[k]})
temp := nums[j]
for nums[j] == temp && j < k {
j++
}
}else if nums[i] + nums[j] + nums[k] < 0{
j++
}else{
k--
}
}
return res
}
这个题目用到了排序,对于goLang排序是sort.Ints(nums),Java是Arrays.sort(nums)
而且不同于Java,twoSum必须要把res返回,这与Java的传引用不同,go是传值的。但是如果不想返回,可以使用指针。
所以这样也行
func threeSum(nums []int) [][]int {
res := [][]int{}
if len(nums) >= 3 {
sort.Ints(nums)
i := 0
for i < len(nums) - 2{
twoSum(nums, i, &res)
temp := nums[i]
for i < len(nums) && nums[i] == temp{
i++
}
}
}
return res
}
func twoSum(nums []int, i int, res *[][]int) {
j := i+1
k := len(nums)-1
for j < k{
if nums[i] + nums[j] + nums[k] == 0{
*res = append(*res, []int{nums[i], nums[j], nums[k]})
temp := nums[j]
for nums[j] == temp && j < k {
j++
}
}else if nums[i] + nums[j] + nums[k] < 0{
j++
}else{
k--
}
}
}
剑指 Offer II 008. 和大于等于 target 的最短子数组
func minSubArrayLen(target int, nums []int) int {
left := 0
sum := 0
minlen := math.MaxFloat64
for right := 0 ; right < len(nums) ; right++{
sum = sum + nums[right]
for left <= right && sum >= target {
minlen = math.Min(minlen, float64(right-left+1))
sum = sum - nums[left]
left++
}
}
if minlen == math.MaxFloat64 {
return 0
}
return int(minlen)
}
这里要注意minlen = math.Min(minlen, float64(right-left+1)) 的输入输出都是float64,所以minlen := math.MaxFloat64 取的是float64,最终返回值是int,所以要强转。
剑指 Offer II 009. 乘积小于 K 的子数组
这个不难,由于给定的是正整数,所以只要从左到右乘进去,然后再从左到右除,一个O(n)的时间复杂度就可以搞定。
一个很基础的双指针问题。
func numSubarrayProductLessThanK(nums []int, k int) int {
product := 1
left := 0
count := 0
for right := 0 ; right < len(nums) ; right++{
product *= nums[right]
for left <= right && product >= k{
product /= nums[left]
left++// 注意left是跟随向右走的
}
if right >= left{
count += right-left+1
}else{
count += 0
}
}
return count
}
2.3 累加数组数字求子数组之和
剑指 Offer II 010. 和为 k 的子数组
这个有个问题,求连续数组的和,那不能排序;整数数组,可能有负数,所以不能用双指针,因为可能越加越少;
所以要用哈希表,存sumToCount,
func subarraySum(nums []int, k int) int {
sumToCount := make(map[int]int, len(nums))
sumToCount[0] = 1
sum := 0
count := 0
for _, num := range nums{
sum += num
v, ok := sumToCount[sum-k]
if ok{
count += v // 存在一个sum-k,就加一个,这里是为了统计结果
}
// v, ok = sumToCount[sum]
// if !ok{
// v = 0
// }
// 用这一行,代替上边一段,因为如果获取结果失败,刚好把v设为默认值0
v, _ = sumToCount[sum]
sumToCount[sum] = v+1 //这里才是统计sum
}
return count
}
剑指 Offer II 011. 0 和 1 个数相同的子数组
func findMaxLength(nums []int) int {
sumToIndex := make(map[int]int, len(nums))
sumToIndex[0] = -1
sum := 0
maxLength := 0
for i := 0 ; i < len(nums) ; i++{
if nums[i] == 0{
sum += -1
}else{
sum += 1
}
if index, ok := sumToIndex[sum]; ok{
temp := i - index
if maxLength < temp {
maxLength = temp
}
}else{
sumToIndex[sum] = i
}
}
return maxLength
}
关键点,累加到当前位置得到一个sum,判断是否之前已经有过这样一个累加值。如果有,那么到到上一个下标之间这段就是最长子数组。
goLang的一个语法糖,if 可以用;分割,分号之后做判断条件。
剑指 Offer II 012. 左右两边子数组的和相等
func pivotIndex(nums []int) int {
total := 0
for _, num := range nums {
total += num
}
sum := 0
for i, length := 0, len(nums); i < length; i++ {
sum += nums[i]
if( sum-nums[i] == total - sum){
return i
}
}
return -1
}
注意Java中求数组和可以用流,例如:
int total = Arrays.stream(nums).sum();
剑指 Offer II 013. 二维子矩阵的和
这个题没有调试出来!!!
后来应该是用chatGPT改出来的,但是不知道为什么csdn没有保存上一次的修改记录
type NumMatrix struct {
sums [][]int
}
func Constructor(matrix [][]int) NumMatrix {
rows, cols := len(matrix), len(matrix[0])
sums := make([][]int, rows+1)
for i := range sums {
sums[i] = make([]int, cols+1)
}
for i := 1; i <= rows; i++ {
for j := 1; j <= cols; j++ {
sums[i][j] = matrix[i-1][j-1] + sums[i-1][j] + sums[i][j-1] - sums[i-1][j-1]
}
}
return NumMatrix{sums: sums}
}
func (this *NumMatrix) SumRegion(row1 int, col1 int, row2 int, col2 int) int {
return this.sums[row2+1][col2+1] - this.sums[row1][col2+1] - this.sums[row2+1][col1] + this.sums[row1][col1]
}
/**
* Your NumMatrix object will be instantiated and called as such:
* obj := Constructor(matrix);
* param_1 := obj.SumRegion(row1,col1,row2,col2);
*/
3、字符串
3.2 双指针
剑指 Offer II 014. 字符串中的变位词
3.3 回文字符串
剑指 Offer II 018. 有效的回文
func isPalindrome(s string) bool {
left := 0
right := len(s)-1
for left < right {
ch1 := s[left]
ch2 := s[right]
if !isLetterOrDigit(ch1) {
left++
} else if !isLetterOrDigit(ch2) {
right--
}else {
ch1 = toLowerCase(ch1)
ch2 = toLowerCase(ch2)
if ch1 != ch2{
return false
}
left++
right--
}
}
return true
}
func isLetterOrDigit(ch byte) bool{
if (byte('A') <= ch && ch <= byte('Z')) || (byte('a') <= ch && ch <= byte('z')) || (byte('0') <= ch && ch <= byte('9')) {
return true
}
return false
}
func toLowerCase(ch byte) byte{
if byte('A') <= ch && ch <= byte('Z'){
return ch - byte('A') + byte('a')
}
return ch
}
字符处理:
判断是字母或数字
Java
Character.isLetterOrDigit(ch);
goLang
需要自己判断
主要goLang的字符是byte
字符串中取某个值
Java
str.charAt(index);
goLang
ch1 := s[left]
剑指 Offer II 019. 最多删除一个字符得到回文
func validPalindrome(s string) bool {
left := 0
right := len(s)-1
for left < right {
ch1 := s[left]
ch2 := s[right]
if ch1 != ch2 {
s1 := s[left: right]
s2 := s[left+1: right+1]
fmt.Println(s1)
fmt.Println(s2)
return (isPalindrome(s1) || isPalindrome(s2))
}
left++
right--
}
return true
}
func isPalindrome(s string) bool {
left := 0
right := len(s)-1
for left < right {
ch1 := s[left]
ch2 := s[right]
if !isLetterOrDigit(ch1) {
left++
} else if !isLetterOrDigit(ch2) {
right--
}else {
ch1 = toLowerCase(ch1)
ch2 = toLowerCase(ch2)
if ch1 != ch2{
return false
}
left++
right--
}
}
return true
}
func isLetterOrDigit(ch byte) bool{
if (byte('A') <= ch && ch <= byte('Z')) || (byte('a') <= ch && ch <= byte('z')) || (byte('0') <= ch && ch <= byte('9')) {
return true
}
return false
}
func toLowerCase(ch byte) byte{
if byte('A') <= ch && ch <= byte('Z'){
return ch - byte('A') + byte('a')
}
return ch
}
剑指 Offer II 020. 回文子字符串的个数
func countSubstrings(s string) int {
if s == "" || len(s) == 0 {
return 0
}
count := 0
for i := 0 ; i < len(s) ; i++{
count += countPalindrome(s, i, i)
count += countPalindrome(s, i, i+1)
}
return count
}
func countPalindrome(s string, start int, end int) int {
count := 0
for start >= 0 && end < len(s) && s[start] == s[end] {
count++
start--
end++
}
return count
}
书中Java代码第一个边界判断是判断s==null
,如果go写s==nil
就有问题,因为string不匹配nil,string是有默认值的,如果需要判空,那么就写s==""
。
4、链表
4.3 双指针
剑指 Offer II 021. 删除链表的倒数第 n 个结点
/**
* Definition for singly-linked list.
* type ListNode struct {
* Val int
* Next *ListNode
* }
*/
func removeNthFromEnd(head *ListNode, n int) *ListNode {
dummy := &ListNode{
0,
nil,
}
dummy.Next = head
front := head
back := dummy
for i := 0 ; i < n ; i++ {
front = front.Next // 为什么可以一直next,因为条件是 n <= sz
}
for front != nil {
front = front.Next
back = back.Next
}
back.Next = back.Next.Next
return dummy.Next
}
最初对dummy初始化的时候写成了
dummy := ListNode{
0,
nil,
}
这导致了dummy是个ListNode,但是他的Next是指针,在执行back = back.Next
的时候失败,利用chatGPT找到了问题。
剑指 Offer II 022. 链表中环的入口节点
/**
* Definition for singly-linked list.
* type ListNode struct {
* Val int
* Next *ListNode
* }
*/
func detectCycle(head *ListNode) *ListNode {
inLoop := getNodeInLoop(head)
if inLoop == nil {
return nil
}
node := head
for node != inLoop {
node = node.Next
inLoop = inLoop.Next
}
return node
}
func getNodeInLoop(head *ListNode) *ListNode {
if head == nil || head.Next == nil {
return nil
}
slow := head.Next
fast := slow.Next
for slow != nil && fast != nil {
if slow == fast {
return slow
}
slow = slow.Next
fast = fast.Next
if fast != nil {
fast = fast.Next
}
}
return nil
}
快慢指针判断是否成环,然后一个从头,一个从交汇地往前走,再次交汇就是入口
剑指 Offer II 023. 两个链表的第一个重合节点
这个是第一次涉及到结构体,第一次出错特别多,直接在程序中注释
/**
* Definition for singly-linked list.
* type ListNode struct {
* Val int
* Next *ListNode
* }
*/
func getIntersectionNode(headA, headB *ListNode) *ListNode {
count1 := countList(headA)
count2 := countList(headB)
delta := math.Abs(count1-count2) // math.Abs()的入参和返回值都是float64
var longer *ListNode // 为了要将headA赋给longer,longer也应该是一个指针
var shorter *ListNode
if count1 > count2 { longer = headA } else { longer = headB} // goLang没有三元表达式,必须强制使用if
if count1 > count2 { shorter = headB } else { shorter = headA}
node1 := longer
for i := 0.0 ; i < delta; i++{
node1 = node1.Next
}
node2 := shorter
for node1 != node2 {
node1 = node1.Next
node2 = node2.Next
}
return node1
}
func countList(head *ListNode) float64{
count := 0.0
for head != nil {
count++
head = head.Next
}
return count
}
4.4 反转链表
剑指 Offer II 024. 反转链表
/**
* Definition for singly-linked list.
* type ListNode struct {
* Val int
* Next *ListNode
* }
*/
func reverseList(head *ListNode) *ListNode {
var prev *ListNode // prev := nil不可以
cur := head
for cur != nil {
next := cur.Next
cur.Next = prev
prev = cur
cur = next
}
return prev
}
剑指 Offer II 025. 链表中的两数相加
/**
* Definition for singly-linked list.
* type ListNode struct {
* Val int
* Next *ListNode
* }
*/
func addTwoNumbers(l1 *ListNode, l2 *ListNode) *ListNode {
l1 = reverseList(l1)
l2 = reverseList(l2)
reversedHead := addReversed(l1, l2)
return reverseList(reversedHead)
}
func reverseList(head *ListNode) *ListNode {
var prev *ListNode // prev := nil不可以
cur := head
for cur != nil {
next := cur.Next
cur.Next = prev
prev = cur
cur = next
}
return prev
}
func addReversed(head1 *ListNode, head2 *ListNode) *ListNode {
dummy := &ListNode{
0,
nil,
}
sumNode := dummy
carry := 0
for head1 != nil || head2 != nil {
sum := 0
if head1 != nil {
sum += head1.Val
}
if head2 != nil {
sum += head2.Val
}
sum += carry
if sum >= 10 {
carry = 1
}else {
carry = 0
}
if sum >= 10 {
sum = sum - 10
}
newNode := &ListNode{
sum,
nil,
}
sumNode.Next = newNode
sumNode = sumNode.Next
if head1 != nil {
head1 = head1.Next
}
if head2 != nil {
head2 = head2.Next
}
}
if carry > 0 {
sumNode.Next = &ListNode{
carry,
nil,
}
}
return dummy.Next
}
剑指 Offer II 026. 重排链表
/**
* Definition for singly-linked list.
* type ListNode struct {
* Val int
* Next *ListNode
* }
*/
func reorderList(head *ListNode) {
dummy := &ListNode{
0,
head,
}
fast, slow := dummy, dummy
for fast != nil && fast.Next != nil {
slow = slow.Next
fast = fast.Next
if fast.Next != nil {
fast = fast.Next
}
}
temp := slow.Next
slow.Next = nil
link(head, reverseList(temp), dummy)
}
func link(node1, node2, head *ListNode) {
prev := head
for node1 != nil && node2 != nil {
temp := node1.Next
prev.Next = node1
node1.Next = node2
prev = node2
node1 = temp
node2 = node2.Next
}
if node1 != nil {
prev.Next = node1
}
}
func reverseList(head *ListNode) *ListNode {
var prev *ListNode // prev := nil不可以
cur := head
for cur != nil {
next := cur.Next
cur.Next = prev
prev = cur
cur = next
}
return prev
}
剑指 Offer II 027. 回文链表
/**
* Definition for singly-linked list.
* type ListNode struct {
* Val int
* Next *ListNode
* }
*/
func isPalindrome(head *ListNode) bool {
if head == nil || head.Next == nil {
return true
}
slow := head
fast := head.Next
for fast.Next != nil && fast.Next.Next != nil {
fast = fast.Next.Next
slow = slow.Next
}
secondHalf := slow.Next
if fast.Next != nil{
secondHalf = slow.Next.Next // 当有奇数个节点,舍弃掉第一条链的最后一个
}
slow.Next = nil // 断开第一条和第二条的连接
return equals(secondHalf, reverseList(head))
}
func reverseList(head *ListNode) *ListNode {
var prev *ListNode
cur := head;
for cur != nil {
next := cur.Next
cur.Next = prev
prev = cur
cur = next
}
return prev
}
func equals(head1, head2 *ListNode) bool{
for head1 != nil && head2 != nil {
if head1.Val != head2.Val{
return false
}
head1 = head1.Next
head2 = head2.Next
}
return head1 == nil && head2 == nil
}
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
public boolean isPalindrome(ListNode head) {
if (head == null || head.next == null){
return true;
}
ListNode slow = head;
ListNode fast = head.next;
while (fast.next != null && fast.next.next != null){
fast = fast.next.next;
slow = slow.next;
}
ListNode secondHalf = slow.next;
if (fast.next != null){
secondHalf = slow.next.next; // 当有奇数个节点,舍弃掉第一条链的最后一个
}
slow.next = null; // 断开第一条和第二条的连接
return equals(secondHalf, reverseList(head));
}
public ListNode reverseList(ListNode head) {
ListNode prev = null;
ListNode cur = head;
while (cur != null){
ListNode next = cur.next;
cur.next = prev;
prev = cur;
cur = next;
}
return prev;
}
private boolean equals(ListNode head1, ListNode head2){
while (head1 != null && head2 != null){
if (head1.val != head2.val){
return false;
}
head1 = head1.next;
head2 = head2.next;
}
return head1 == null && head2 == null;
}
// class ListNode {
// int val;
// ListNode next;
//
// ListNode(int x) {
// val = x;
// next = null;
// }
// }
}
思考:为什么这里要判断边界,24题不需要,仅仅是因为返回值不同吗?这里返回boolean,24返回链表
这个题用了快慢指针,快慢指针通常用于解决链表成环的问题,例如22题。
4.5 双向链表和循环链表
剑指 Offer II 028. 展平多级双向链表
/**
* Definition for a Node.
* type Node struct {
* Val int
* Prev *Node
* Next *Node
* Child *Node
* }
*/
func flatten(root *Node) *Node {
flattenGetTail(root)
return root
}
func flattenGetTail(head *Node) *Node {
node := head
tail := &Node{
0, nil, nil, nil,
}
for node != nil {
next := node.Next
if node.Child != nil {
child := node.Child
childTail := flattenGetTail(node.Child)
node.Child = nil
node.Next = child
child.Prev = node
childTail.Next = next
if next != nil {
next.Prev = childTail
}
tail = childTail
}else {
tail = node
}
node = next
}
return tail
}
剑指 Offer II 029. 排序的循环链表
/**
* Definition for a Node.
* type Node struct {
* Val int
* Next *Node
* }
*/
func insert(head *Node, x int) *Node {
node := &Node{
x, nil,
}
if head == nil {
head = node
head.Next = head
}else if head.Next == head {
head.Next = node
node.Next = head
}else {
insertCore(head, node)
}
return head
}
func insertCore(head, node *Node) {
cur, next, biggest := head, head.Next, head
for !(cur.Val <= node.Val && next.Val >= node.Val) && next != head {
cur = next
next = next.Next
if cur.Val >= biggest.Val {
biggest = cur
}
}
if cur.Val <= node.Val && next.Val >= node.Val {
cur.Next = node
node.Next = next
}else {
node.Next = biggest.Next
biggest.Next = node
}
}
5、哈希表
5.2 哈希表的设计
剑指 Offer II 030. 插入、删除和随机访问都是 O(1) 的容器
先贴chatGPT给的正确答案
这里要特别注意随机数生成的写法,否则很容易报
panic: invalid argument to Intn
math/rand.(*Rand).Intn(0x8?, 0x4b4560?)
rand.go, line 168
math/rand.Intn(...)
rand.go, line 337
main.(*RandomizedSet).GetRandom(0xc00005cd80)
solution.go, line 45
main.(*DriverSolution).Helper_Select_Method(0x4b76e0?, {0xc000014080?, 0x0?}, {0x5ac9f8?, 0x7?, 0x8?}, 0xc00005cd80?)
solution.go, line 74
main.(*DriverSolution).Helper(0xc0000560c0?, {0xc000076120, 0x8, 0x4b3ee0?}, {0xc000084000, 0x8, 0xc000014038?})
solution.go, line 126
main.main()
solution.go, line 171
这个错误和导包没关系,leetcode会自动导包。
type RandomizedSet struct {
numToLocation map[int]int
nums []int
}
/** Initialize your data structure here. */
func Constructor() RandomizedSet {
numToLocation := make(map[int]int)
nums := make([]int, 0)
rand.Seed(time.Now().UnixNano())
return RandomizedSet{numToLocation, nums}
}
/** Inserts a value to the set. Returns true if the set did not already contain the specified element. */
func (this *RandomizedSet) Insert(val int) bool {
_, ok := this.numToLocation[val]
if ok {
return false
}
this.numToLocation[val] = len(this.nums)
this.nums = append(this.nums, val)
return true
}
/** Removes a value from the set. Returns true if the set contained the specified element. */
func (this *RandomizedSet) Remove(val int) bool {
loc, ok := this.numToLocation[val]
if !ok {
return false
}
this.numToLocation[this.nums[len(this.nums)-1]] = loc
delete(this.numToLocation, val)
this.nums[loc] = this.nums[len(this.nums)-1]
this.nums = this.nums[:len(this.nums)-1]
return true
}
/** Get a random element from the set. */
func (this *RandomizedSet) GetRandom() int {
loc := rand.Intn(len(this.nums))
return this.nums[loc]
}
剑指 Offer II 031. 最近最少使用缓存
type LRUCache struct {
head *LRUListNode
tail *LRUListNode
lruMap map[int]*LRUListNode
capacity int
}
type LRUListNode struct {
Key int
Val int
Next *LRUListNode
Prev *LRUListNode
}
func Constructor(capacity int) LRUCache {
head := &LRUListNode{-1, -1, nil, nil}
tail := &LRUListNode{-1, -1, nil, head}
head.Next = tail
lruMap := make(map[int]*LRUListNode)
return LRUCache{head, tail, lruMap, capacity}
}
func (this *LRUCache) Get(key int) int {
node, ok := this.lruMap[key]
if !ok {
return -1
}
this.moveToTail(node, node.Val)
return node.Val
}
func (this *LRUCache) moveToTail(node *LRUListNode, newVal int) {
this.deleteNode(node)
node.Val = newVal
this.insertToTail(node)
}
func (this *LRUCache) deleteNode(node *LRUListNode) {
node.Prev.Next = node.Next
node.Next.Prev = node.Prev
}
func (this *LRUCache) insertToTail(node *LRUListNode) {
this.tail.Prev.Next = node
node.Prev = this.tail.Prev
node.Next = this.tail
this.tail.Prev = node
}
func (this *LRUCache) Put(key int, value int) {
if _, ok := this.lruMap[key]; ok {
this.moveToTail(this.lruMap[key], value)
} else {
if len(this.lruMap) == this.capacity {
toBeDeleted := this.head.Next
this.deleteNode(toBeDeleted)
delete(this.lruMap, toBeDeleted.Key)
}
node := &LRUListNode{key, value, nil, nil}
this.insertToTail(node)
this.lruMap[key] = node
}
}
/**
* Your LRUCache object will be instantiated and called as such:
* obj := Constructor(capacity);
* param_1 := obj.Get(key);
* obj.Put(key,value);
*/
5.3 哈希表的应用
剑指 Offer II 032. 有效的变位词
func isAnagram(s string, t string) bool {
if len(s) != len(t) || s == t{
return false
}
counts := [26]int{} // 初始化一个数组
for _, ch := range s { // 遍历一个字符串
counts[ch-'a']++
}
for _, ch := range t {
if counts[ch-'a'] == 0{
return false
}
counts[ch-'a']--
}
return true
}
剑指 Offer II 034. 外星语言是否排序
func isAlienSorted(words []string, order string) bool {
orderArray := [26]int{}
for i := 0 ; i < len(order) ; i++{
orderArray[order[i]-'a'] = i
}
for i := 0 ; i < len(words)-1 ; i++{ // 注意边界
if !isSorted(words[i], words[i+1], orderArray){
return false
}
}
return true
}
func isSorted(word1,word2 string, order [26]int) bool{
i := 0
for ; i < len(word1) && i < len(word2) ; i++{
ch1 := word1[i];
ch2 := word2[i];
if order[ch1-'a'] < order[ch2-'a']{
return true
}
if order[ch1-'a'] > order[ch2-'a']{
return false
}
}
return i == len(word1) // 前缀相同,长度不同。如果等于第一个的长度,那说明第一个遍历结束,第一个比第二个短,也就是第一个在前
}
7、队列
7.1 基础知识
Queue的方法
操作 | 抛异常 | 不抛异常 |
---|---|---|
插入元素 | add(e) | offer(e) |
删除元素 | remove | poll |
返回最前面的元素 | element | peek |
可以看出Java处理队列问题真简单,但是goLang没有队列。。。
goLang处理队列问题通常是切片或者链表,大概相当与ArrayList和LinkedList
参考文章golang的两种队列实现方式
7.2 队列的应用
剑指 Offer II 041. 滑动窗口的平均值
class MovingAverage {
private Queue<Integer> nums;
private int capacity;
private int sum;
/** Initialize your data structure here. */
public MovingAverage(int size) {
nums = new LinkedList<>();
capacity = size;
}
public double next(int val) {
nums.offer(val);
sum += val;
if (nums.size() > capacity){
sum -= nums.poll();
}
return (double) sum / nums.size();
}
}
/**
* Your MovingAverage object will be instantiated and called as such:
* MovingAverage obj = new MovingAverage(size);
* double param_1 = obj.next(val);
*/
type MovingAverage struct {
queue []int
cap int
sum int
}
/** Initialize your data structure here. */
func Constructor(size int) MovingAverage {
var ins = MovingAverage{
make([]int, 0, size+2),
size,
0,
}
return ins
}
func (this *MovingAverage) Next(val int) float64 {
this.queue = append(this.queue, val)
this.sum += val
if len(this.queue) > this.cap {
this.sum -= this.queue[0]
this.queue = this.queue[1:this.cap+1]
}
return float64(this.sum)/float64(len(this.queue))
}
/**
* Your MovingAverage object will be instantiated and called as such:
* obj := Constructor(size);
* param_1 := obj.Next(val);
*/
这真的是一种奇怪的写法,通过切片的截取实现了和队列一样的先进先出效果,我朋友跟我讲切片就是队列,那就先这样吧。
这里要注意几个点:
初始化结构体的时候,最后一个元素后边还要加",",我不知道只是goLand的特性还是就该如此。
切片的截取则是前包后不包,所以cap要+1
剑指 Offer II 042. 最近请求次数
import java.util.LinkedList;
import java.util.Queue;
//leetcode submit region begin(Prohibit modification and deletion)
class RecentCounter {
private Queue<Integer> times;
private int windowSize;
public RecentCounter() {
times = new LinkedList<>();
windowSize = 3000;
}
public int ping(int t) {
times.offer(t);
while (times.peek() + windowSize < t){
times.poll();
}
return times.size();
}
}
/**
* Your RecentCounter object will be instantiated and called as such:
* RecentCounter obj = new RecentCounter();
* int param_1 = obj.ping(t);
*/
//leetcode submit region end(Prohibit modification and deletion)
type RecentCounter struct {
times []int
windowSize int
}
func Constructor() RecentCounter {
var ins = RecentCounter{
make([]int, 0, 10000),
3000,
}
return ins
}
func (this *RecentCounter) Ping(t int) int {
this.times = append(this.times, t)
for this.times[0] + this.windowSize < t {
this.times = this.times[1:]
}
return len(this.times)
}
/**
* Your RecentCounter object will be instantiated and called as such:
* obj := Constructor();
* param_1 := obj.Ping(t);
*/
搞明白了41题,42题就很容易做了,就是个用切片代替Java-Queue的做法。
8、树
8.3 二叉搜索树
剑指 Offer II 052. 展平二叉搜索树
class Solution {
public TreeNode increasingBST(TreeNode root) {
Stack<TreeNode> stack = new Stack<>();
TreeNode cur = root;
TreeNode prev = null;
TreeNode first = null;
while (cur != null || !stack.isEmpty()) {
// 已示例1为例,从栈底到栈顶依次是5321
while (cur != null) {
stack.push(cur);
cur = cur.left;
}
cur = stack.pop();
if (prev != null) {
// 只有初始化的时候prev才是null,之后总是有值的
prev.right = cur;
} else {
// 这里只会进来一次,然后指向最左子节点
first = cur;
}
prev = cur;
cur.left = null;
cur = cur.right;
}
return first;
}
class TreeNode {
int val;
TreeNode left;
TreeNode right;
TreeNode() {
}
TreeNode(int val) {
this.val = val;
}
TreeNode(int val, TreeNode left, TreeNode right) {
this.val = val;
this.left = left;
this.right = right;
}
}
}
/**
* Definition for a binary tree node.
* type TreeNode struct {
* Val int
* Left *TreeNode
* Right *TreeNode
* }
*/
func increasingBST(root *TreeNode) *TreeNode {
stack := make([]*TreeNode, 0, 0)
cur := root
var prev *TreeNode
var first *TreeNode
for cur != nil || len(stack) != 0{
for cur != nil{
stack = append(stack, cur)
cur = cur.Left
}
cur = stack[len(stack)-1]
stack = stack[:len(stack)-1]
if prev != nil{
prev.Right = cur
}else{
first = cur
}
prev = cur
cur.Left = nil
cur = cur.Right
}
return first
}
这是第一次用切片来模拟栈,也是第一次使用指针。
用切片当栈用倒不复杂,出栈的时候len-1把最后一个舍弃掉就可以了。
指针初始化要记住形式 var first * TreeNode。
11、二分查找
11.2 在排序树组中二分查找
剑指 Offer II 068. 查找插入位置
func searchInsert(nums []int, target int) int {
left := 0
right := len(nums)-1
for left <= right{
mid := (left+right)/2
if nums[mid] >= target && (mid == 0 || nums[mid-1] < target){
return mid
}
if nums[mid] >= target {
right = mid - 1
}else{
left = mid + 1
}
}
return len(nums)
}
很基础的二分查找
剑指 Offer II 069. 山峰数组的顶部
func peakIndexInMountainArray(arr []int) int {
left := 1
right := len(arr) - 2
for left <= right{
mid := (left + right)/2
if arr[mid] > arr[mid+1] && arr[mid] > arr[mid-1]{
return mid
}
if arr[mid] > arr[mid-1]{
left = mid+1
} else {
right = mid-1
}
}
return -1
}
很基础的二分查找
11.3 在数值范围内二分查找
剑指 Offer II 072. 求平方根
func mySqrt(x int) int {
left := 1
right := x
for left <= right {
mid := (left + right)/2
if mid <= x / mid && (mid+1) > x / (mid+1) { // 注意:mid*mid <= x 数学上等价,但是会溢出
return mid
}
if mid <= x / mid{
left = mid + 1
}else{
right = mid - 1
}
}
return 0
}
}
12、排序
12.2 计数排序
剑指 Offer II 075. 数组相对排序
func relativeSortArray(arr1 []int, arr2 []int) []int {
var counts [1001]int
for _, num := range arr1 {
counts[num]++
}
i := 0
for _, num := range arr2{
for counts[num] > 0{
arr1[i] = num
i++
counts[num]--
}
}
for num := 0 ; num < len(counts) ; num++{
for counts[num] > 0 {
arr1[i] = num
i++
counts[num]--
}
}
return arr1
}
注意range的写法, 是:= range
并且,arr1[i++]的写法也是错误的
总结
提示:这里对文章进行总结:
例如:以上就是今天要讲的内容,本文仅仅简单介绍了pandas的使用,而pandas提供了大量能使我们快速便捷地处理数据的函数和方法。
更多推荐
leetcode-剑指offer(专项突破版)-go语言实现
发布评论