【Lua】排序算法分析与实现

顺序插入排序:

--- 顺序插入排序
---@param arr 需要排序的表
local InsertSort = function(arr)
	for i = 2, #arr do
		local j = i - 1
		local tmp = arr[i]
		
		--采用顺序查找法查找插入位置
		if arr[i] < arr[j] then
			while j >= 1 and tmp < arr[j] do
				arr[j + 1] = arr[j]--移动元素
				j = j - 1
			end
			
			--插入正确位置
			arr[j + 1] = tmp
		end
	end
	return arr
end

折半插入排序:

--- 折半插入排序
---@param arr 需要排序的表
local BLinsertSort = function(arr)
	for i = 2, #arr do
		local tmp = arr[i]
		local low, hight, mid = 1, i - 1, 0

		--采用二分查找法查找插入位置
		while low <= hight do
			mid = math.floor((low+hight)/2)
			if tmp < arr[mid] then
				hight = mid - 1
			else
				low = mid + 1
			end
		end--循环结束arr[hight + 1]则为插入位置

		--移动元素
		local j = i - 1
		while j > 0 and j > hight do
			arr[j+1] = arr[j]
			j = j - 1
		end

		--插入正确位置
		arr[hight + 1] = tmp
	end
	return arr
end

希尔排序:

--- 希尔排序算法
---@param arr 需要排序的表
---@param dlta 增量因子表
local ShellSort = function(arr, dlta)
	for k = 1, #dlta do
		--对arr进行一趟增量为dk的Shell排序,dk为步长因子
		local dk = dlta[k]
		for i = dk+1, #arr, 1 do
			if arr[i] < arr[i - dk] then
				local tmp = arr[i]
				local j = i - dk
				
				--顺序查找 间隔为dk
				while j > 0 and tmp < arr[j] do
					arr[j + dk] = arr[j]
					j = j - dk
				end

				--插入正确位置
				arr[j + dk] = tmp
			end
		end
	end
	return arr
end

冒泡排序:

--- 冒泡排序
---@param arr 需要排序的表
local BubbleSort = function(arr)
	--flag 作为是否有交换的标记
	local flag = 1
	for i = 1, #arr - 1 do
		if flag == 0 then return arr end
		flag = 0
		for j = 1, #arr - i do
			if arr[j] > arr[j+1] then
				--发生交换,flag置为1,若本趟没发送交换,说明排序完成,flag保持为0
				flag = 1
				arr[j], arr[j+1] = arr[j+1], arr[j]
			end
		end
	end
	return arr
end

快速排序:

local Sort, Partition

--- 快速排序
---@param arr 需要排序的表
---@param low 表的最低索引
---@param hight 表的最高索引
Sort = function(arr, low, hight)
	--长度大于1
	if low < hight then
		--取arr表中心位置,用于将表分为两个子表
		local pivot = Partition(arr, low, hight)

		--将arr[low...hight] 一分为二,pivot为中心枢轴元素排好序的位置
		Sort(arr, low, pivot - 1)	--对小于pivot子表递归排序
		Sort(arr, pivot + 1, hight)	--对大于pivot子表递归排序
	end
	return arr
end

--- 将枢轴移动到表(arr)中心位置
---@param arr 子表
---@param low 表的最低索引
---@param hight 表的最高索引
Partition = function(arr, low, hight)
	--取low为枢轴(中心)
	local pivotKey = arr[low]
	--子表长度大于1
	while low < hight do
		--从后往前遍历,小于枢轴的值则--hight
		while low < hight and arr[hight] >= pivotKey do
			hight = hight - 1
		end
		--移动到子表低位
		arr[low] = arr[hight]

		--从前往后遍历,大于枢轴的值则++low
		while low < hight and arr[low] <= pivotKey do
			low = low + 1
		end
		--移动到子表高位
		arr[hight] = arr[low]
	end--遍历后得到low == hight的位置,就是此子表的中心位置
	
	--赋值中心位置
	arr[low] = pivotKey
	return low
end

--调用
Sort(arr, 1, #arr)

简单选择排序:

--- 简单选择排序
---@param arr 需要排序的表
local SelectSort = function(arr)
	for i = 1, #arr do
		--选择每一趟中最小值的下标
		local idx = i
		for j = i + 1, #arr do
			--记录最小值下标
			idx = arr[idx] > arr[j] and j or idx
		end

		--交换
		if i ~= idx then
			arr[i],arr[idx] = arr[idx],arr[i]
		end
	end
	return arr
end

堆排序:

--- 堆排序
---@param arr 需要排序的表
local HeapSort = function(arr)
	local len = #arr
	--建初始堆(升序一般用大根堆,降序一般用小根堆)
	local i = math.floor(len/2)
	while i >= 1 do
		HeapAdjust(arr, i, len)
		i = i - 1
	end

	--进行n - 1趟排序
	for j = len, 1, -1 do
		--跟与最后一个元素交换
		arr[1], arr[j] = arr[j], arr[1]
		--对arr[1]到arr[i - 1]重新堆排序
		HeapAdjust(arr, 1, j - 1)
	end
	return arr
end

--- 堆筛选
---@param arr 需要筛选的表
---@param s 根结点
---@param m 最后一个叶子节点
local HeapAdjust = function(arr, s, m)
	--已知arr[s..m]中记录的关键字除arr[s]之外均满足堆的定义,本函数调整arr[s]的关键字
	--使得arr[s..m]成为一个大根堆
	local tmp = arr[s]
	local j = 2*s

	--沿着值较大的孩子结点向下筛选
	while j <= m do
		--记录值较大的孩子下标
		if j < m and arr[j] < arr[j+1] then j = j + 1; end
		--根结点大于最大孩子值,则无需变换
		if tmp >= arr[j] then break end
		arr[s] = arr[j]
		--tmp应插入在s位置上
		s = j
		j = 2 * j
	end
	--插入正确位置
	arr[s] = tmp
end

各排序方法的综合比较:

一、时间性能
1、按平均的时间性能来分,有三类排序方法:

  • 时间复杂度为O(nlogn)的有:快速排序、堆排序和归并排序,其中以快速排序为最好
  • 时间复杂度为O(n²)的有:直接插入排序、冒泡排序和简单排序,其中以直接插入为最好,特别是对那些对关键字近似的记录序列尤为如此
  • 时间复杂度为O(n)的有:基数排序

2、当待排记录按关键字顺序有序时,直接插入排序和冒泡排序能达到O(n)的时间复杂度;而对于快速排序而言。这是最不好的情况,此时的时间性能退化为O(n²),因此是应该尽量的情况
3、简单选择排序、堆排序和归并排序的时间性能不随记录序列中关键字的发布而改变

二、空间性能
1、所有的简单排序方法(直接插入、冒泡和简单选择)和堆排序的空间复杂度为O(1)
2、快速排序为O(logn),为栈所需的辅助空间
3、归并排序所需辅助空间最多,其空间复杂度为O(n)
4、链式基数排序需附设队列收尾指针,则空间复杂度为O(rd)

三、排序方法的稳定性能
1、稳定的排序方法指的是,对于两个关键字相等的记录,他们在序列中的相对位置,在排序之前和经过排序之后,没有改变
2、当对多关键字的记录序列进行LSD方法排序,必须采用稳定的排序方法
3、对于不稳定的排序方法,只要能举例出一个实例即可说明
4、快速排序和堆排序是不稳定的排序方法

四、关于排序方法的时间复杂度的下限
1、初基数排序外,其他方法都是基于”比较关键字“进行排序的排序方法,可以证明,折类排序方法可能达到最快的时间复杂度为O(nlogn),基数排序不是基于”比较关键字“的排序方法,所以它不受这个限制
2、可以用一个判定树来描述这类基于”比较关键字“进行排序的排序方法

五、排序方法比较

排序方法时间复杂度空间复杂度稳定性
直接插入排序O(n²)O(1)稳定
希尔排序~O(n^1.3)O(1)不稳定
冒泡排序O(n²)O(1)稳定
快速排序O(nlogn)O(nlogn)不稳定
直接选择排序O(n²)O(1)不稳定
堆排序O(nlogn)O(n²)不稳定
选择排序O(nlogn)O(n)稳定
基数排序O(k*(n+m))O(n+m)稳定
  • 基数排序中k代表待排元素的维数,m为基数的个数
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
快速排序是一种高效的排序算法,它的基本思想是通过一次排序将数组分成两个子数组,其中一个子数组的所有元素都比另一个子数组的所有元素小,然后再分别对这两个子数组递归地进行快速排序。 以下是使用Lua语言实现快速排序的示例代码: ``` function quickSort(arr, left, right) left = left or 1 right = right or #arr if left >= right then return end local pivotIndex = partition(arr, left, right) quickSort(arr, left, pivotIndex - 1) quickSort(arr, pivotIndex + 1, right) end function partition(arr, left, right) local pivot = arr[right] local i = left - 1 for j = left, right - 1 do if arr[j] < pivot then i = i + 1 arr[i], arr[j] = arr[j], arr[i] end end arr[i + 1], arr[right] = arr[right], arr[i + 1] return i + 1 end -- 测试 local arr = {3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5} quickSort(arr) for i, v in ipairs(arr) do print(v) end ``` 在上面的代码中,quickSort函数接收一个数组arr和两个可选参数left和right作为参数,其中left和right分别表示需要排序的子数组的左右边界,如果没有传递这两个参数,默认将整个数组排序。在快速排序中,我们首先通过partition函数将数组分成两个子数组,然后再对这两个子数组递归地进行快速排序。 在partition函数中,我们选择数组中的最后一个元素作为pivot,然后通过一次循环将数组分成两个子数组,其中一个子数组的所有元素都比pivot小,另一个子数组的所有元素都比pivot大。最后,将pivot交换到子数组的中间位置,返回pivot的索引。 在测试代码中,我们使用了一个随机数组进行测试,并打印出排序后的结果。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值