经典排序算法学习

排序稳定性

假定在待排序的记录序列中,存在多个具有相同的关键字的记录,若经过排序,这些记录的相对次序保持不变,即在原序列中,r[i]=r[j],且r[i]在r[j]之前,而在排序后的序列中,r[i]仍在r[j]之前,则称这种排序算法是稳定的;否则称为不稳定的

意义

有时候我们需要按照多个条件排序,比如举重比赛,按照成绩排序,成绩相同,按照体重的逆序排序。
那么如果排序算法是稳定的,我们可以先按照体重逆序排序后再按照成绩排序,则结果就是我们要的。如果是不稳定排序,我们需要额外的步骤保证结果的正确。

快速排序

QuickSort
Golang实现

package main

import "fmt"

func main() {
	nums := []int{7, 2, 6, 4, 1, 9, 4}
	QuickSort(nums)
	fmt.Print(nums)
}

func QuickSort(nums []int) {
	n := len(nums)
	if n <= 1 {
		return
	}
	key := nums[0]
	i := 0
	j := n - 1
	for i != j {
		for ; j >= i; j-- {
			if nums[j] <= key {
				nums[i], nums[j] = nums[j], nums[i]
				break
			}
		}
		for ; i < j; i++ {
			if nums[i] > key {
				nums[i], nums[j] = nums[j], nums[i]
				break
			}
		}
	}
	QuickSort(nums[:i])
	if i < n {
		QuickSort(nums[:i+1])
	}
}

要点
算法思想:找出一个数key,大于key的放右边,小于key的放左边。如何放?使用交换的办法:
即 key = nums[0], 然后 i = 0, j = n-1
先进行j的循环,判断nums[j]<key,真则nums[j],nums[i]交换,假则 j-- 直到真break
再进行i的循环,判断nums[i]>key,真则nums[i],nums[j]交换,假则i++直到真break
若i==j,则不再进行上面两个步骤,否则执行上面两个步骤
剩下的部分,左边部分 QuickSort(left) ,右边的部分 QuickSort(right)

归并排序

和QuickSort 快速排序 非常像
但是 QuickSort 会把左右两边排列一下,但是归并排序不做这个操作
在这里插入图片描述
图片来源:https://www.cnblogs.com/piperck/p/6030122.html

归并排序就是把表不断二分,直到二分到最小单元(一个数)
然后切成很多最小的块之后,再拼接起来
拼接的方式是,排序拼接
A, B 两个表
选取A中元素a, 选取B中元素b,作比较把小的放入C
最后返回C

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)
}

堆排序

建立一个堆,比如小顶堆
即每个节点,都比其子节点小

堆怎么建立起来的?
n = len(nums)-1
通过root=n/2 然后子树是root2+1、root2+2
然后对(root, root2+1, root2+2) 这棵子树做调整
首先child 等于 root2+1, root2+2较小的子树
然后child和root作比较,要是child比较小,则root,child数据交换
然后root 等于 child, 再此进行调整

通过转移root+for循环来持续调整

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

通过上面的代码就建立了小顶堆
然后对小顶堆进行排序
因为小顶堆的堆顶也就是 nums[0] 一定是最小的
那么对 1 ~ n 进行建立小顶堆,建立完成后,nums[1]是剩下的数的堆顶,是最小的,然后对 2 ~ n 进行建立小顶堆…
这就是升序的排法
降序就是反过来,代码实现:

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)
		}
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值