快速排序和归并排序

前言:在使用排序算法时,我发现对于数组和切片类型的数据,我能够非常熟练地使用快速排序。然而,当涉及到链表排序时,我只能实现最基本的冒泡排序和快速排序。但相比之下,我认为归并排序在处理链表时效率更高。因此,我希望通过这次回顾,更系统地掌握数组和链表的不同排序方式,尤其是归并排序的应用。并且,在最后,我会介绍一下我实际运用切片时遇到的问题。

快速排序

在这里插入图片描述

(图像来源:https://www.cnblogs.com/onepixel/p/7674659.html)

func q_sort(nums []int) {
	if len(nums) <= 1 {
		return
	}

	left, right := 0, len(nums)-1
	pivot := nums[len(nums)/2]

	for left <= right {
		for left <= right && nums[left] < pivot {
			left++
		}
		for left <= right && nums[right] > pivot {
			right--
		}
		if left <= right {
			nums[left], nums[right] = nums[right], nums[left]
			left++
			right--
		}
	}

	if right > 0 {
		q_sort(nums[:right+1])
	}
	if left < len(nums) {
		q_sort(nums[left:])
	}
}

归并排序

在这里插入图片描述
(图像来源:https://www.cnblogs.com/onepixel/p/7674659.html)

在这里插入图片描述
(图像来源:https://www.hello-algo.com/chapter_sorting/merge_sort/)

func mergeSort(head *ListNode) *ListNode {
	if head == nil || head.Next == nil {
		return head
	}

	mid := findMiddle(head)
	right := mid.Next
	mid.Next = nil

	l := mergeSort(head)
	r := mergeSort(right)

	return merge(l, r)
}

func findMiddle(head *ListNode) *ListNode {
	slow, fast := head, head
	var prev *ListNode

	for fast != nil && fast.Next != nil {
		prev = slow
		slow = slow.Next
		fast = fast.Next.Next
	}
	return prev
}

func merge(l1, l2 *ListNode) *ListNode {
	if l1 == nil {
		return l2
	}
	if l2 == nil {
		return l1
	}
	if l1.Val < l2.Val {
		l1.Next = merge(l1.Next, l2)
		return l1
	} else {
		l2.Next = merge(l1, l2.Next)
		return l2
	}
}

案例:力扣算法题 148. 排序链表

题目链接:https://leetcode.cn/problems/sort-list/description/?envType=study-plan-v2&envId=top-100-liked

在这里插入图片描述

代码示例

/**
 * Definition for singly-linked list.
 * type ListNode struct {
 *     Val int
 *     Next *ListNode
 * }
 */
func sortList(head *ListNode) *ListNode {
    if head==nil||head.Next==nil{
        return head
    }
    mid:=findMid(head)
    right:=mid.Next
    mid.Next=nil

    l:=sortList(head)
    r:=sortList(right)
    return merge(l,r)
}


func findMid(head *ListNode)*ListNode{
    slow,fast:=head,head
    var prev *ListNode
    /*
		  for fast!=nil&&fast.Next!=nil
		  这里注意,我第一次不小心写为,fast.Next!=nil&&fast!=nil 
		  这一种写法是错的,因为在遍历的过程 fast 是在前面的,可能出现fast==nil的情况了
		  所以 fast!=nil && fast.Next!=nil ,就可以通过短路机制,不再走后边的条件了
	*/
    for fast!=nil&&fast.Next!=nil{
        prev=slow
        slow=slow.Next
        fast=fast.Next.Next
    }
    return prev
}

func merge(l1, l2 *ListNode)*ListNode{
    if l1==nil{
        return l2
    }
    if l2==nil{
        return l1
    }
    if l1.Val<l2.Val{
        l1.Next=merge(l1.Next,l2)
        return l1
    }else{
        l2.Next=merge(l1,l2.Next)
        return l2
    }
}

关于切片做参数进行append时遇到的bug

问:请写出一下程序运行的答案?

func main() {
	doAppend := func(s []int) {
		s = append(s, 1)
		printSliceStruct(s)
	}
	s := make([]int, 8, 8)
	doAppend(s[:4])
	printSliceStruct(s)
	doAppend(s)
	printSliceStruct(s)
}

func printSliceStruct(s []int) {
	fmt.Println(s)
	fmt.Printf("len=%d cap=%d\n", len(s), cap(s))
}

这里主要就是思考,切片做参数传递的时候,传递的是对底层数组的引用还是因为append增加数据导致扩容,而分配新的内存空间了。

在这里插入图片描述
以及以下实际中遇到的问题:

在这里插入图片描述
这是因为在闭包中引用了外部的变量task,go1.22之后,在for range中避免了task复用同一个地址变量,但是闭包会捕获并引用task这个变量,所以为了避免循环引用task,就进行重新赋值。
而且for range也不能遍历string类型的变量。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

大华Coding

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值