首页 > 其他分享 >Golang 刷题记录

Golang 刷题记录

时间:2023-07-13 20:11:44浏览次数:43  
标签:元素 记录 list nums List Element Golang func 刷题

刷了大概 50 道题,我个人的结论:在中等难度题中,使用 Golang 的效率完全是不输于 C++的,特别是在 Golang 没有 C++这么完善的 STL 库的情况下,只借助 container 包里的三种数据结构,大部分题时间、空间复杂度都可以媲美 C++。当然这个 hard 题是啥情况我这个蒟蒻就不知道咯。

再刷洛谷试炼场,发现试炼场已经无了。开始以为找不到了,知乎上看到站长的回复才知道真的无了。我是从这个人整理的版本里刷的:https://www.luogu.com.cn/paste/66uuuvdr 用 Go 大概刷了 10 道题之后,放弃洛谷。原因是洛谷几乎都是 C++选手。

继续用 Go 在 LeetCode 刷题。遇到什么写什么吧。纯纯的笔记。非专题性内容。

输入输出

才发现用 go 刷题连输入输出都不会

  • print 在golang中 是属于输出到标准错误流中并打印,官方不建议写程序时候用它。可以debug时候用

  • fmt.print 在golang中 是属于标准输出流,一般使用它来进行屏幕输出.

处理字符串

func replaceSpace(s string) string {
	var res strings.Builder
    for i:=range s{
    	if s[i]==' '{
			res.WriteString("%20")
		}else {
			res.WriteByte(s[i])
		}
	}
	return res.String()
}

快慢指针

快指针在前面探路,慢指针满足条件再走。

eg. 删除数组、链表中的重复元素

283 删除0

我的方法:遇到 非0 就填充,最后把没填充的再填充 0;

更优:遇到非 0,就交换。这样 0 会被不停地往后换。

前缀和

快速算区间和

img

Sum(nums[i]--nums[j]) = preSum[j+1]-preSum[i]

  • 二维数组前缀和 https://leetcode-cn.com/problems/range-sum-query-2d-immutable/submissions/

    func Constructor(matrix [][]int) NumMatrix {
    	l := len(matrix)+1
    	preSum := make([][]int, l)
    	for i:=0;i<len(preSum);i++{
    		preSum[i] = make([]int, l)
    		for j:=0;j<len(preSum[i]);j++ {
    			if i == 0 || j == 0{
    				preSum[i][j] = 0
    				continue
    			}
          //注意最后这个 - 
    			preSum[i][j] = preSum[i-1][j] + preSum[i][j-1]+ matrix[i-1][j-1] - preSum[i-1][j-1]
    			//println(i,j,":",preSum[i-1][j],preSum[i][j-1],matrix[i-1][j-1],"-",preSum[i-1][j-1],"=",preSum[i][j])
    		}
    	}
    	return NumMatrix{
    		Matrix: matrix,
    		PreSum: preSum,
    	}
    }
    
    
    func (this *NumMatrix) SumRegion(row1 int, col1 int, row2 int, col2 int) int {
    	//println(this.PreSum[row2+1][col2+1] , this.PreSum[row1][col2+1] , this.PreSum[row2][col1] , this.PreSum[row1][col1])
      // row1和 col1 都是不+1,row2 和 col2 都+1
    	return this.PreSum[row2+1][col2+1] - this.PreSum[row1][col2+1] -
    		this.PreSum[row2+1][col1] + this.PreSum[row1][col1]
    }
    
    

差分数组

☆☆☆频繁对区间进行删减的情况☆☆☆

img

  • nums[i] = diff[i] + nums[i-1]

  • nums[i..j] 都 +3 等于 diff[i]+=3 , diff[j+1]-=3

    但是注意, nums 更新最后一个数字时,diff[j+1]会越界,所以不用再更新

  • 遍历 diff 数组即可还原 nums 数组。

1094. 拼车

1109.航班预订统计

回文

二分

直接二分搜索

// 关键两点
for i<=j { //1. <=
  m = i+1  //2, 边界 +1 / -1
  m = j-1
}

二分一定记得不要重复使用已经判断过的边界m,下次循环一定要m+1、m-1

34.在排序数组中查找元素的第一个和最后一个位置(中等)

二分变形

通过二分查找试出最佳值

  • 取整技巧: (x + n -1)/n == int(ceil(x/n))

滑动窗口

我写的滑动窗口有思想问题

比如找无重复的最长子串,滑动[i..j],我是 外循环 j++,j 不停地往后,发现重复则把 i 更新到出现过的位置。清空 map 重新记录一次。

标准的思想:[i..j],外循环是 i ++, 每次以 i 为开头,内循环 j ++ 尝试找出 以 i 开头最长的。

每次外循环 i++时 删除 map 里的前一个元素 delete(m, s[i-1])

container

go 里没有栈。但container 包里的 list、heap和 ring 也勉强够用。

只是用起来稍微有点绕(特别是不能定义容器的元素类型,遇到返回 Value 的题还要断言一下)

下面遇到某个 container 就整理一下

list

链表就是一个有 prev 和 next 指针的数组了。它维护两个 type,( 注意,这里不是 interface)

type Element struct {
    next, prev *Element  // 上一个元素和下一个元素
    list *List  // 元素所在链表
    Value interface{}  // 元素
}

type List struct {
    root Element  // 链表的根元素
    len  int      // 链表的长度
}

基本使用是先创建 list,然后往 list 中插入值,list 就内部创建一个 Element,并内部设置好 Element 的 next,prev 等。具体可以看下例子:

// This example demonstrates an integer heap built using the heap interface.
package main

import (
    "container/list"
    "fmt"
)

func main() {
    list := list.New()
    list.PushBack(1)
    list.PushBack(2)

    fmt.Printf("len: %v\n", list.Len())
    fmt.Printf("first: %#v\n", list.Front())
    fmt.Printf("second: %#v\n", list.Front().Next())
}

output:
len: 2
first: &list.Element{next:(*list.Element)(0x2081be1b0), prev:(*list.Element)(0x2081be150), list:(*list.List)(0x2081be150), Value:1}
second: &list.Element{next:(*list.Element)(0x2081be150), prev:(*list.Element)(0x2081be180), list:(*list.List)(0x2081be150), Value:2}

list 对应的方法有:

type Element
    func (e *Element) Next() *Element
    func (e *Element) Prev() *Element
type List
    func New() *List
    func (l *List) Back() *Element   // 最后一个元素
    func (l *List) Front() *Element  // 第一个元素
    func (l *List) Init() *List  // 链表初始化
    func (l *List) InsertAfter(v interface{}, mark *Element) *Element // 在某个元素后插入
    func (l *List) InsertBefore(v interface{}, mark *Element) *Element  // 在某个元素前插入
    func (l *List) Len() int // 在链表长度
    func (l *List) MoveAfter(e, mark *Element)  // 把 e 元素移动到 mark 之后
    func (l *List) MoveBefore(e, mark *Element)  // 把 e 元素移动到 mark 之前
    func (l *List) MoveToBack(e *Element) // 把 e 元素移动到队列最后
    func (l *List) MoveToFront(e *Element) // 把 e 元素移动到队列最头部
    func (l *List) PushBack(v interface{}) *Element  // 在队列最后插入元素
    func (l *List) PushBackList(other *List)  // 在队列最后插入接上新队列
    func (l *List) PushFront(v interface{}) *Element  // 在队列头部插入元素
    func (l *List) PushFrontList(other *List) // 在队列头部插入接上新队列
    func (l *List) Remove(e *Element) interface{} // 删除某个元素

下面演示一个模拟栈的 pop

x,_ := this.stack.Remove(this.stack.Back()).(int)
return x

使用注意

this.Lst.MoveToFront(ele)
不等于
this.Lst.Remove(ele)
this.Lst.PushFront(ele)
为啥呢?
看Remove()源码得知,这里面为了防止内存泄漏,在 remove ele后就会吧 e 的指针全部置nil,这样 e 就已经作废了。没法再进行PushFront

还有这里
if ele.Prev() == nil {
		//	head, update value
			p := ele.Value.(pair)
			p.v = value
  这里并不能改变 p 的 v 值
		} else {
			// remove and insert to head
			this.Lst.Remove(ele)
			this.InsertToHead(key, value)
		}

heap

  • 注意,heap 只是保证顶部是最小。不会保证按顺序输出是最小

  • 实现 interface 时只需要注意 Pop() 方法的写法 ,比如最简单的 []int 类型:

    func (p *PriorityQ) Pop() interface{} {
    	n := len(*p)
    	old := *p
    	x := old[n-1]
    	*p = old[:n-1]
    	return x
    }
    

单调栈、单调队列

「单调栈」 Next Great Number 类问题

「单调队列」滑动窗口相关的问题

二叉树

114.二叉树展开为链表(中等)

这里有一点需要注意就是递归的去处理 root 的左右子树 x 和 y 时,

root.Left = nil #左子树指向 nil
root.Right = x # 右指向 x 没毛病
# x.Right = y #这样就错了
应该是循环往下找到最右下角的节点z,把 z 的right 指向 y
# 比如下图, root = 1, x=2, y=5,1.right=2 没毛病,但不应该 2.right=5,应该是 4.right=5
		 1
	/ 		\
  2			 5
   \      \
    3		   6
     \
      4

root.Left = nil

二叉搜索树的变更

三种情况

  • 叶子节点,没啥牵挂
  • 一个孩子,方便
  • 俩孩子,不好管理

BFS

LeetCode752,我的 BFS 为啥超时呢。

  • 首先,queue队列用数组就可以实现,没必要用container 包里的 list(但这个影响也不大)

  • 比较重要的一点,慢的 BFS 是用了层序遍历思想,每次把同级别的符合条件的都入队,

    其实这样也可以实现。但是这会导致什么情况:

    • 入队的数量呈指数级别上升。比如这次入队 2 个,下次入队就这 2 个的下一种情况4 个,然后就是 8、16

      最终就会发现队列超级长

    而只需要每次都只取队列里的头部 1 个!1 个!1 个元素!,这样就大大降低了队列的长度。(只是循环次数会增加)

    但是有一说一。我只分析出这两种方案这里会不同。还是没法分析出为啥那个就可以秒出结果。

回溯

  • 全排列

go 的这个切片这一块,回滚是真的麻烦。

比如你先把第 i 个元素给 remove,再接 i 回来就得这样:

x:=nums[i] //存下 i
nums = append(nums[:i], nums[i+1:]...) //删了 i
f(nums, one, ans)
nums = append(nums[:i],append([]int{x},nums[i:]...)...) //再接上,关键这个接上也太扯淡了

没办法,要么就重新开辟一个切片做深拷贝:

# 深拷贝笨方法
for j,k:=0,0; j<len(nums); j++ {
  if j==i {
    continue
  }
  tmp[k] = nums[j]
  k++
}
# 深拷贝
slice1 := []int{1,2,3,4,5}
slice2 := make([]int,5,5)
copy(slice2,slice1)

这里注意一个切片append的细节:

x := []int{1,2,3}
y := append(x[:0], x[1:]...)
out("x", x) //Notice
out("y", y)
println(len(x))
输出:
x:2 3 3 注意 x 本身也已经发生了改变,说明啥,虽然append后赋给了新变量 y ,但操作还是基于x的内存地址的。切片全是引用操作。
y:2 3 
len(x): 3

DP

有最优子结构的问题:

  • 可以【自顶向下】,使用递归来做。拆分问题。其中递归的过程里可以使用【记忆化】的技巧,避免多次计算。

    更进一步

  • 直接【自底向上】,使用一个结果数组,一步步推导上去。比如求ans[n],我从 ans[0]开始一直求到ans[n],反正你上面递归的方法也是一层层都求过一遍的。

排序

go 倒序

sort.Sort(sort.Reverse(sort.IntSlice(coins)))

标签:元素,记录,list,nums,List,Element,Golang,func,刷题
From: https://www.cnblogs.com/xuanshan/p/17551990.html

相关文章

  • 记录
    亚稳态(由边沿提取温故)定义触发器输出信号无法在规定时间内输出确定的状态同步电路定义钟控单元全由一个统一的全局时钟控制避免竞争冒险触发器只在时钟边沿变取值,减少毛刺和噪声缺点到达各个触发器有延迟时钟可能抖动延时单元消耗面积,功耗大异步电路定......
  • 记录--用Echarts打造自己的天气预报!
    这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助前言最近刚刚学习了Echarts的使用,于是想做一个小案例来巩固一下。项目效果如下图所示:话不多说,开始进入实战。创建项目这里我们使用vue-cli来创建脚手架:vuecreateapp这里的app是你要创建的项目的名称,进入......
  • WSL2安装CUDA记录
    ❗建议直接Windows安装驱动,不要在子系统里装官网安装,选择自己的版本即可https://developer.nvidia.com/cuda-downloads?target_os=Linux&target_arch=x86_64&Distribution=Ubuntu&target_version=22.04&target_type=deb_localwget<https://developer.download.nvidia.com/co......
  • Jmeter运用记录
    1、jmeter环境变量配置博主写的非常清楚:https://blog.csdn.net/vi_xiao/article/details/122090157cmd进入命令行窗口,分别输入java-version(验证是否安装jdk)、jmeter(验证jmeter是否安装成功) 2、启动方式:方式一:进入JMeter的安装路径,打开bin文件,在bin文件夹下找到“jmeter......
  • Kubernetes轻量级日志工具Loki安装及踩坑记录
    Loki简介Loki是Grafana出品的一个轻量级日志系统,熟悉ELK的都知道ELK使用起来的成本,而且仅仅是日志检索使用ELK的话有点大材小用了。Loki8技术栈中使用了以下组件。Promtail用来将容器日志发送到Loki或者Grafana服务上的日志收集工具,该工具主要包括发现采集目标以及给日志......
  • pwn | buuctf刷题记录(二)
    babyheap_0ctf_2017堆溢出,extendoverlap,unsortedbinleak,fastbinattackedit选项可以随意溢出堆块,通过扩展堆块造成重叠,把后面一个bins的fd给打出来,从而泄露libc,通过fastbinattack将malloc_hook改为one_gadgetextendoverlap1.首先申请四个堆块,note0用于溢出修改note1的si......
  • Golang学习笔记-数据类型
    目录整型有符号整型无符号整型栗子浮点型栗子布尔型栗子字符型栗子字符串型栗子字符串<-->其他类型转换数组栗子切片创建切片通过数组的方式创建切片读取/修改切片元素追加元素-append合并多个切片删除元素字典栗子字典读、写、删除指针栗子方法栗子结构体结构体继承接口栗子类型......
  • 算法小菜鸟成长记录Day01-二分查找和双重指针
    二分查找和双重指针今天是代码随想录刷题的第一天,刚开始刷的时候昏昏欲睡,其中用时3h主要实现以下几个部分二分查找:其中二分查找中其收获最大部分就在于对左开右闭区间的理解,如果都是闭区间也就是【a,b】,那么在while中的条件就为while(left<=right),确保其中是拥有元素也就是......
  • 20230710刷题
    B.ObsessionwithRobots先假设除了机器人走的路其他的地方都是障碍,然后记录下来可以走的地方用BFS遍历一遍,判断一个机器人有没有bug#include<bits/stdc++.h>#defineyescout<<"YES"<<'\n'#defineno cout<<"NO"<<'\n'usingnamespacest......
  • 暑期记录5
    7月11之前新买的车子,没怎么骑,一骑就发现变速器有问题我靠,自己和老爸修了老半天,不是剐蹭就是升不上去挡位。下午就去修了,结果他也没修好我靠,之后又去一个车子专卖店那里去修,结果老板直接说,某某变速我不修,修起来很麻烦。​那一天真烦,后来找卖家,卖家联系技术人员和我通话,教我一步......