go实现复杂度与排序算法

本文详细介绍了各种排序算法的时间复杂度和空间复杂度,如选择排序、冒泡排序和插入排序,并探讨了位运算在优化算法中的作用。此外,还讨论了在有序数组中查找元素和移除元素的高效方法,以及如何在有限空间内实现这些操作。
摘要由CSDN通过智能技术生成

时间复杂度

时间复杂度是常数操作地指标
数组:偏移量直接寻址搞定
链表:需要一个一个地去找,不能通过偏移量去找
常数操作地表达式,不要低阶项,也不要高阶项地系数,只保留高阶项,就是时间复杂度
复杂度越小越好
当复杂度一致地时候,需要拼常数项或者直接用程序运行走一把

func pro1() {
	n := 1000
	for i := 0; i < n; i++ {
		n = 1 + 1
		n = 1 * 1
		n = 3 * 3
	}
}
func pro2() {
	n := 1000
	for i := 0; i < n; i++ {
		n = 1 | 1
		n = 1 & 1
		n = 3 ^ 3
	}
}

常数操作

跟数据量无关地,固定时间地东西
加减乘除
数组
位运算

非常数操作

跟数据量有关地
底层api
链表

额外空间复杂度

额外有限几个变量就可以完成,复杂度为O(1)
如果需要开辟数组,则为O(n)

选择排序

每次在未排序地数组中找到一个最小的,与当前数交换位置
在这里插入图片描述

在这里插入图片描述

func selectsort(arr []int) []int {
	temp := len(arr)
	if temp < 2 || arr == nil { // 写程序前先考虑过滤杂项
		return nil
	}
	for i := 0; i < temp; i++ {
		minindex := i
		for j := i + 1; j < temp; j++ {
			if arr[minindex] > arr[j] {
				minindex = j
			}

		}
		arr[minindex], arr[i] = arr[i], arr[minindex]
	}
	return arr
}

prac1
package main

import "fmt"

func selectsort(arr []int) []int {
	temp := len(arr)
	if temp < 2 || arr == nil {
		return nil
	}
	//每次在未排序的数组中找到一个最小的与当前值进行交换
	// 当前值
	// 与剩下值做对比
	// 外循环每次取值,内循环每次跟剩下的值做对比
	for i := 0; i < temp; i++ {
		minIndex := i
		for j := i + 1; j < temp; j++ {
			if arr[minIndex] > arr[j] {
				minIndex = j
			}
		}
		arr[minIndex], arr[i] = arr[i], arr[minIndex]
	}

	return arr
}

//选择排序:每次在未排序的数组中找到一个最小的与当前值进行交换
func main() {
	// arr := []int{2, 5, 6, 3, 1, 0}
	arr := []int{5, 2}
	fmt.Println(selectsort(arr))
}

时间复杂度:O(n^2)
一个for循环为o(n),嵌套for循环相乘即可
空间复杂度:O(1)
为了实现此算法,额外定义了temp,i,j,minindex,使用的空间有限,所以为O(1)

冒泡排序

每一次当前值跟后一个值做对比,当前值大于后一个值,就交换位置,简称沉底
在这里插入图片描述

在这里插入图片描述

func bubblesort(arr []int) []int {
	temp := len(arr)
	if temp < 1 || arr == nil {
		return nil
	}
	for i := 0; i < temp; i++ {
		for j := 0; j < temp-i-1; j++ {
			if arr[j] > arr[j+1] {
				arr[j], arr[j+1] = arr[j+1], arr[j]
			}
		}
	}
	return arr
}
practice1
package main

import "fmt"

func bubblesort(arr []int) []int {
	temp := len(arr)
	if temp < 2 || arr == nil {
		return nil
	}
	//冒泡排序
	//每一次将最大的一个值沉底
	//拿每次值与剩下的值做对比,如果大了就交换
	for i := 0; i < temp; i++ {
		for j := 0; j < temp-i-1; j++ {
			if arr[j] > arr[j+1] {
				arr[j+1], arr[j] = arr[j], arr[j+1]
			}
		}
	}
	return arr
}

func main() {
	arr := []int{2, 5, 3, 1, 6, 9}
	fmt.Println(bubblesort(arr))
}

时间复杂度:一共需要n次循环,一共需要比较n次,所以时间复杂度为O(n^2)
空间复杂度:有限的几个变量,则为O(1)

位运算

无进位相加
能用位运算的就用位运算,别问为什么
在这里插入图片描述
在这里插入图片描述
a和b在内存里是两块独立的区域
a = 10
b = 10
查看a与b的地址

	a := 10
	b := 10
	fmt.Println(&a, &b) // 0xc0000160a8 0xc0000160c0

// 思考
	a := 10
	b := 10
	fmt.Println(&a, &b) //0xc0000aa078 0xc0000aa090
	a, b = b, a
	fmt.Println(&a, &b) //0xc0000aa078 0xc0000aa090

在这里插入图片描述

一种数

func printOddTimesNum1(arr []int) int {
	value := 0
	for _, eor := range arr {
		value ^= eor
	}
	return value
}

func main() {
	nums := []int{1, 3, 3, 2, 2, 6, 6}
	fmt.Println(printOddTimesNum1(nums))
}

时间复杂度:O(n)
空间负载的:O(1)

两种数

func printOddTimesNum2(arr []int) (int, int) {
	value := 0
	for _, eor := range arr { //首先把这两个数找到
		value ^= eor
	}
	// 取出一个数最右侧的1 将这个数&这个数取反在加1
	rightone := value & (-value + 1)
	for _, eor := range arr {
		if (eor & rightone) != 0 { // 将尾数是1的拿出来
			rightone ^= eor // 此操作可以拿到其中的一个数
		}
	}
	return value ^ rightone, rightone
}

func main() {
	nums := []int{1, 3, 3, 2, 2, 6, 6, 9}
	fmt.Println(printOddTimesNum2(nums))
}

paactice1
package main

import "fmt"

/*
有个整型数组,时间O(N),空间O(1)
1):一种数奇数次,其他数偶数次,求奇数次的数
2):两种数奇数次,其他数偶数次,求奇数次的数
*/

func eventimeone(arr []int) int {
	temp := len(arr)

	if temp < 2 || arr == nil {
		return -1
	}
	eor := 0
	for _, i := range arr {
		eor = eor ^ i
	}
	return eor
}

func eventimetwo(arr []int) int {
	temp := len(arr)
	if temp < 2 || arr == nil {
		return -1
	}
	// 两种数奇,other 偶
	// 将所有数^,得到这两个数的^值
	// 将得到的这个值拿出最右边的1,在跟原数组进行^操作
	// 如果
	eor := 0
	for _, i := range arr {
		eor ^= i
	}
	fmt.Println("eor:=", eor)
	//取出一个数最右侧的1
	rightone := eor & (^eor + 1)
	fmt.Println("rightone", rightone)
	eorling := 0
	for _, i := range arr {
		if (rightone & i) != 0 {
			eorling ^= i
		}
	}

	fmt.Println(eorling, eor^eorling)
	return eor
}
func printOddTimesNum2(arr []int) (int, int) {
	value := 0
	for _, eor := range arr { //首先把这两个数找到
		value ^= eor
	}
	// 取出一个数最右侧的1 将这个数&这个数取反在加1
	rightone := value & (^value + 1)
	fmt.Println(rightone)
	for _, eor := range arr {
		if (eor & rightone) != 0 { // 将尾数是1的拿出来
			rightone ^= eor // 此操作可以拿到其中的一个数
		}
	}
	return value ^ rightone, rightone
}

func main() {
	// arr := []int{1, 2, 2, 3, 3, 5, 5}
	// fmt.Println(eventimeone(arr))
	arrs := []int{1, 2, 3, 3, 5, 5, 6, 6}
	fmt.Println(eventimetwo(arrs))
	// fmt.Println(printOddTimesNum2(arrs))
}

在这里插入图片描述

时间复杂度:O(n + n) = O(n)
空间复杂度:O(1)

位运算要看八位
在这里插入图片描述

在这里插入图片描述
这个算法目前存在的问题是当奇数个数的数全是奇数的时候,有点问题,后面继续改进

在这里插入图片描述

插入排序

每一次向当前值往前到第一个,将小的值放在前面
在这里插入图片描述

func insertsort(arr []int) []int {
	temp := len(arr)
	if temp < 2 || arr == nil {
		return nil
	}
	for i := 0; i < temp; i++ {
		for j := i; j > 0; j-- {
			if arr[j] < arr[j-1] {
				arr[j], arr[j-1] = arr[j-1], arr[j]
			}
		}
	}
	return arr
}
practice1
package main

import "fmt"

func insertsort(arr []int) []int {
	temp := len(arr)
	if temp < 2 || arr == nil {
		return nil
	}
	//插入排序
	//每一次跟当前值前面的所有值做对比,小的放前面,大的不变
	// 外循环每次的值
	// 内循环每次跟前面所有值做对比
	for i := 0; i < temp; i++ {
		for j := i; j > 0; j-- {
			if arr[j] < arr[j-1] {
				arr[j], arr[j-1] = arr[j-1], arr[j]
			}
		}

	}
	return arr
}

func main() {
	arr := []int{2, 5, 7, 3, 6}
	fmt.Println(insertsort(arr))
}

二分法

有序数组中查找某个数是否存在

在这里插入图片描述

func bsexitone(arr []int, target int) int {
	temp := len(arr)
	if temp < 2 || arr == nil {
		return -1
	}
	//有序数组中查找某一个数
	// 采用二分的方法,那么首先应该定义二分在哪,然后分条件讨论

	L := 0
	R := temp
	for L < R { //找到两个边界值,当边界值超出的时候,说明没找到
		mid := L + (R-L)>>1     // 取数组的中点使用位运算
		if arr[mid] == target { //如果刚好找到这个值,结束
			return mid
			// return arr[mid]
		} else if arr[mid] < target { //如果target的值大于当前值,说明需要往右取
			L = mid
		} else { //如果target的值小于当前值,说明需要往左取
			R = mid
		}
	}
	return -1
}

有序数组中,找>=某个数最左侧的位置

在这里插入图片描述

局部最小值问题

在这里插入图片描述在这里插入图片描述

对数器

递归

在这里插入图片描述
取中点用位运算
mid = L + (R-L) >> 1
在这里插入图片描述
master:特殊行为适用
在这里插入图片描述

归并排序

小和问题

逆序对

荷兰国旗

快排

1.0
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
2.0
在这里插入图片描述
在这里插入图片描述
3.0

逻辑概念上是一颗完全二叉树
在这里插入图片描述

大根堆

小根堆

daily practice

二分查找:

func search(nums []int, target int) int {
	if len(nums) < 1 || nums == nil {
		return -1
	}
	L := 0
	R := len(nums) - 1
	mid := 0
	for L <= R {
		mid = L + (R-L)>>1
		if nums[mid] > target {
			R = mid - 1
		} else if nums[mid] < target {
			L = mid + 1
		} else if nums[mid] == target {
			return mid
		}
	}
	return -1
}

时间复杂度:每一次都折半查找,比如一共查找八次,则就是2^n = 8,求n使用logn即可搞定
空间复杂度:寥寥几个变量,O(1)

移除元素:

package main

import "fmt"

func removeElement1(nums []int, val int) int {
	temp := len(nums)
	count := temp
	newval := temp
	for _, eor := range nums {
		if val == eor {
			temp--
		} else {
			nums = append(nums, eor)
			count += 1
		}
	}
	nums_count := len(nums[newval:])
	for key, eor := range nums[newval:] {
		nums[key] = eor
		if nums[key] == val {
			nums[key] = eor
		}
	}
	fmt.Println(nums)
	nums = nums[:nums_count]
	return temp
}
func removeElement2(nums []int, val int) int {
	temp := len(nums)
	// count := temp
	cc := temp
	for _, eor := range nums {
		if val != eor {
			nums = append(nums, eor)
			cc += 1
		}
	}
	for key, eor := range nums {
		if (key + temp) < cc {
			eor = nums[key+temp]
		} else {
			eor = 0
		}
		fmt.Println(eor)
	}
	fmt.Println(nums)
	return cc
}

func removeElement(nums []int, val int) int {
	left := 0
	for _, v := range nums { // v 即 nums[right]
		if v != val {
			nums[left] = v
			left++
		}
	}
	fmt.Println(nums)
	return left
}

func main() {
	nums := []int{0, 1, 2, 2, 3, 0, 4, 2}
	val := 2
	fmt.Println(removeElement(nums, val))
}

刚开始思路就想的太傻了,刚开始是这样想的
在这里插入图片描述
把与val不相同的一直往后面放,然后在将后面的跟前面的交换位置
直到我看了一眼答案,惊为天人
时间复杂度:O(1)
空间复杂度:O(1)

有序数组的平方:

思路没错但是没做出来版本

package main

import "fmt"

func sortedSquares(nums []int) []int {
	temp := len(nums)
	if temp < 1 || nums == nil {
		return nil
	}
	// 时间复杂度要为O(n),所以选择,插入,冒泡都不可用,二分也不可用
	// 因为是递增的,所以只需要考虑对半即可,也就是考虑负数这一块其实就ok
	// 所以先准备两个数组,一个放负数,一个放正数,然后将复数和正数合并成为一个新数组
	nums_z := []int{}
	nums_f := []int{}

	// 将正负数开始分类
	for _, value := range nums {
		if value < 0 {
			nums_f = append(nums_f, value)
		} else {
			nums_z = append(nums_z, value)
		}
	}
	// 将负数数组全部变为正数
	if nums_f != nil {
		for key, value := range nums_f {
			nums_f[key] = -value
		}
	}
	//合并数组
	flag := true
	nums_count_f := 0
	nums_count_z := 0
	num_acc := []int{}
	for flag {
		if nums_z != nil {
			if nums_z[nums_count_z] > nums_f[nums_count_f] {
				num_acc = append(num_acc, nums_f[nums_count_f])
				nums_count_f += 1
			}
		} else {
			flag = false
		}
		if nums_f != nil {
			if nums_z[nums_count_z] > nums_f[nums_count_f] {
				num_acc = append(num_acc, nums_f[nums_count_f])
				nums_count_z += 1
			}
		} else {
			flag = false
		}
	}

	// 全部平方
	for key, value := range num_acc {
		nums[key] = value * value
	}
	return num_acc

}

func main() {
	nums := []int{-4, -1, 0, 3, 10}
	fmt.Println(sortedSquares(nums))
}

归并排序版本

在这里插入图片描述

func sortedSquares(arr []int) []int {
	temp := len(arr)
	if temp < 2 || arr == nil {
		return nil
	}
	// 找出正数负数的位置
	lastnum := -1
	for i := 0; i < temp; i++ {
		if arr[i] < 0 {
			lastnum = i
		}
	}
	// 根据lastnum也就是小于0的数求出边界值
	ans := []int{}
	// ans := make([]int, 0, temp)
	for i, j := lastnum, lastnum+1; i >= 0 || j < temp; {
		// 根据四个条件,先写第一个
		// 当i也就是负数没有了的时候,数组只能存正数
		if i < 0 {
			ans = append(ans, arr[j]*arr[j])
			j++
		} else if j == temp { //只有负数,没有正数的时候,数组只能存负数
			ans = append(ans, arr[i]*arr[i])
			i--
		} else if (arr[i] * arr[i]) < (arr[j] * arr[j]) { // 当不在两个边界值的时候,判断i和j哪个数小存哪个
			ans = append(ans, arr[i]*arr[i])
			i--
		} else {
			ans = append(ans, arr[j]*arr[j])
			j++
		}
	}
	return ans
}

惊人版

func sortedSquares(arr []int) []int {
	temp := len(arr)
	if temp < 2 || arr == nil {
		return nil
	}
	// 不管正负了,直接双指针,一个从左走,一个从右走
	i, j := 0, temp-1
	// ans := []int{} 当使用这样定义的数组的时候,不能直接赋值
	ans := make([]int, temp) //这样定义的数组可以直接赋值比如arr[3] = 3
	// for pos := 0; pos < temp; pos++ {//因为走的是边界值,所以放的值从最大的开始,所以外循环要改
	for pos := temp - 1; pos >= 0; pos-- { //因为走的是边界值,所以放的值从最大的开始,所以外循环要改
		if k, v := arr[i]*arr[i], arr[j]*arr[j]; k > v {
			// ans = append(ans, k)
			ans[pos] = k
			i++
		} else {
			ans[pos] = v
			j--
		}
	}
	return ans
}

长度最小的子数组:

螺旋矩阵 II:

参考资料

左神算法公开课

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值