前言
冒泡、冒泡改进、鸡尾酒、快速……
话说,用lua做这些算法不会很奇怪吗?也许有lua模块可以进行更快的排序吧!在写这篇也算是学习(复习)一下lua了,在实现了几个排序后可能会加一下特殊点的语法。
(用的是sublime text编译)
基本显示
print('hello lua developer\n基本显示')
a={
1,8,9,10,'a',3,2,6,7,4,5,'hello'} --lua数据结构,表table,可代替类
print(#a)
--[[
多行注释
#a表示求a中的数字或字符串的个数
..表示字符串的拼接
]]
for i=1,#a do --单行注释 默认步长为1
io.write(i..'>') --输出后没换行
for j=1,i do
io.write(a[j]..' ')
end
print() --输出后有换行
end
输出为
hello lua developer
基本显示
12
1>1
2>1 8
3>1 8 9
4>1 8 9 10
5>1 8 9 10 a
6>1 8 9 10 a 3
7>1 8 9 10 a 3 2
8>1 8 9 10 a 3 2 6
9>1 8 9 10 a 3 2 6 7
10>1 8 9 10 a 3 2 6 7 4
11>1 8 9 10 a 3 2 6 7 4 5
12>1 8 9 10 a 3 2 6 7 4 5 hello
[Finished in 0.1s]
冒泡排序
一组乱序的数据,通过两两相邻的交换直到全部排序完成!
就像气泡从水里升起一样!
如果外部索引从头到尾刷过去,那么内部索引就像上面那样,从头到尾且长度逐渐减少。之所以这样是因为第一步就把最大的排到了最后,第二步就把第二大的排到了倒数第二个,所以内部索引刷新的长度就没必要去刷已排序完成的部分了。
如果外部索引的刷新顺序是从尾到头,那么内部索引就相反。
- lua语言是从1开始索引的,但是区别不大!
print('hello lua developer\n冒泡排序')
local function showArray(arr) --局部函数
for i=1,#arr do
io.write(arr[i])
--等距输出
if arr[i]>9 then
io.write(' ')
else
io.write(' ')
end
end
print()
end
arr={
1,8,9,10,3,2,6,7,4,5}
showArray(arr)
for i=1,#arr do
for j=1,#arr-i do
if arr[j]>arr[j+1] then
temp=arr[j]
arr[j]=arr[j+1]
arr[j+1]=temp
end
end
end
showArray(arr)
输出为
hello lua developer
冒泡排序
1 8 9 10 3 2 6 7 4 5
1 2 3 4 5 6 7 8 9 10
[Finished in 0.3s]
冒泡排序改进1,提前结束
内部索引第一轮是从开头刷新到末尾,最后一轮是刷新第一个到第二个,但是如果到最后一轮前,前面两个就已经是排序好的了,那么最后一轮就是浪费!
所以说,需要提前结束排序——在一轮内部刷新没有交换元素的情况下(已经排序完成)
for i=1,#arr do
isSorted=true
for j=1,#arr-i do
if arr[j]>arr[j+1] then
temp=arr[j]
arr[j]=arr[j+1]
arr[j+1]=temp
isSorted=false --有元素交换,没有排序完
end
end
if isSorted then --上一轮没有元素交换,已经排序完成
break
end
end
冒泡排序改进2,设置有序边界
之前是提前结束,算是设置了左边界,现在我们来设置右边界。
每次开始的阶段都是从头到尾,这也不xing哎!我们可以设置从头到sortBorder——比如本来是刷新到7,现在设置了右边界后刷新到4,当然是在5,6,7都排序好时,这样效率就更高了点。
lastExchangeIndex=1
sortBorder=#arr-1
for i=1,#arr do
isSorted=true
for j=1,sortBorder do
if arr[j]>arr[j+1] then
temp=arr[j]
arr[j]=arr[j+1]
arr[j+1]=temp
isSorted=false --有元素交换,没有排序完
lastExchangeIndex=j --更新为最后一次交换元素的位置
end
end
sortBorder=lastExchangeIndex --更新边界
if isSorted then --上一轮没有元素交换,已经排序完成
break
end
end
鸡尾酒排序
左右都在冒泡,左边冒一会儿泡,右边冒一会儿泡,就像摇鸡尾酒一样(大概吧)
鸡尾酒排序就是冒泡排序·改,可以在大部分元素已经有序的情况下,发挥其优势!
for i=1,#arr/2 do
isSorted=true
--从左到右
for j=i,#arr-i do
if arr[j]>arr[j+1] then
temp=arr[j]
arr[j]=arr[j+1]
arr[j+1]=temp
isSorted=false
end
end
if isSorted then
break
end
--从右到左
for j=#arr-i+1,i+1,-1 do
if arr[j-1]>arr[j] then
temp=arr[j]
arr[j]=arr[j-1]
arr[j-1]=temp
isSorted=false
end
end
if isSorted then
break
end
end
鸡尾酒排序改进1,设置有序边界
leftBorder=2
leftLastExchangeIndex=#arr
rightBorder=#arr-1
rightLastExchangeIndex=1
for i=1,#arr/2 do
isSorted=true
--从左到右
for j=leftBorder,rightBorder do
if arr[j]>arr[j+1] then
temp=arr[j]
arr[j]=arr[j+1]
arr[j+1]=temp
isSorted=false
rightLastExchangeIndex=j
end
end
rightBorder=rightLastExchangeIndex
if isSorted then
break
end
--从右到左
for j=rightBorder,leftBorder,-1 do
if arr[j-1]>arr[j] then
temp=arr[j]
arr[j]=arr[j-1]
arr[j-1]=temp
isSorted=false
leftLastExchangeIndex=j
end
end
leftBorder=leftLastExchangeIndex
if isSorted then
break
end
end
快速排序
快速排序有好几种实现的方法,总的来说,利用了分治的思想的排序就是快速排序!
分治类似于二分法,一分二,二分四,四份八
双边循环的快速排序
先从一组数据中取一个做基准元素,一般取第一个或者是随机取一个。
然后设置两个指针,一个从头刷新到尾,一个从尾刷新到头,左边的指针找到大于基准元素的,右边找到小于基准元素的,然后交换一下——这样就可以实现把小于基准元素的都放一边,然后递归下去,就可以把整个数据都排序好!
local function partition(arr,startIndex,endIndex)
--[[
取第一个位置的元素做基准元素
当然,如果第一个元素是最大的或最小的,时间复杂度就很高了
]]
pivot=arr[startIndex]
leftPointer=startIndex --左指针,负责在左边找大于基准元素的
rightPointer=endIndex --右指针,负责在右边找小于基准元素的
while leftPointer~=rightPointer do --lua不等于为~=
while leftPointer<rightPointer and arr[rightPointer]>pivot do
rightPointer=rightPointer-1
end
while leftPointer<rightPointer and arr[leftPointer]<=pivot do
leftPointer=leftPointer+1
end
--找到了,把小的元素放到左边,大的元素放到右边
if leftPointer<rightPointer then
temp=arr[leftPointer]
arr[leftPointer]=arr[rightPointer]
arr[rightPointer]=temp
end
end
--从循环里出来时,leftPointer和rightPointer重合了,所以把基准元素(第一个元素)交换到中间去
arr[startIndex]=arr[leftPointer]
arr[leftPointer]=pivot
--返回中间元素
return leftPointer
end
local function quicksort(arr,startIndex,endIndex)
if startIndex>=endIndex then --递归结束条件,一般都放前面
return
end
--取得基准元素,并排序
pivotIndex=partition(arr,startIndex,endIndex)
--根据基准元素,分成两个部分进行递归
quicksort(arr,startIndex,pivotIndex-1)
quicksort(arr,pivotIndex+1,endIndex)
end
arr={
1,8,9,10,3,2,6,7,4,5}
showArray(arr)
quicksort(arr,1,#arr)
showArray(arr)
输出
hello lua developer
快速排序
1 8 9 10 3 2 6 7 4 5
1 2 3 4 5 6 7 8 9 10
[Finished in 0.1s]
单边循环的快速排序
相似的道理,没必要设置两个指针,一个指针也可以。在左边设置一个指针,从左刷新到右,遇到比基准大的就继续右移,遇到小于基准的就交换到此指针左边去!
local function partition(arr,startIndex,endIndex)
pivot=arr[startIndex]
mark=startIndex
--从第二个元素开始往右刷
for i=startIndex+1,endIndex do
if arr[i]<pivot then --每次遇到一个小于基准的,就把它交换到mark左边
mark=mark+1
temp=arr[mark]
arr[mark]=arr[i]
arr[i]=temp
end
end
--mark指针停住了,意思是已经把小的放在了左边,大的放在了右边
--所以,此时把基准元素交换到mark位置来
arr[startIndex]=arr[mark]
arr[mark]=pivot
return mark
end
非递归的快速排序
递归与栈可以相互代替,所以不用函数递归就用栈的抛出和压进!
先用lua模块编写的方法做一个stack的数据结构,不止可以压入数据,还能压入对象!
这处的难点在于如何拷贝table!
- 我只做了一个简单的保存数据的栈,没有实现保存表的功能┭┮﹏┭┮
--[[
用lua做的数据结构栈module.lua
lua中的基本类型、函数都是值传递,只有表是引用传递
直接用stack=require "stack"来接收模块,可以得到默认的一个空栈
想要拷贝的话就用a=copy(stack)
lua中的#table只算其中的数字和字符串的数量
]]
local module={
}
module.info='stack_2019_12_7@demllie'
module.top=0 --记的是栈中的一级总数,如果某个元素是表,那么下面的元素不计
function push(self,data)
--print('push')
if type(data)~='number' then
print('error:data isn\'t number')
return -1
end
self.top=self.top+1
self[self.top]=data
end
function pop(self)
--print('pop')
if self.top==0 then
print('error:top is zero, can\'t pop data')
return -1
end
data=self[self.top]
table.remove(self) --默认删除最后一个元素
self.top=self.top-1
return data
end
function isEmpty(self)
--print('isEmpty')
return self.top==0
end
function getNum(self)
return self.top
end
function showArray(self)
io.write('showArray>')
for i=1,#self do
io.write(self[i])
--等距输出
if self[i]>9 then
io.write(' ')
else
io.write(' ')
end
end
print()
end
--拷贝一个表
function copy(self)
local function table_copy(src, dst)
for k,v in pairs(src) do
if type(v) == "table" then
dst[k] = {
}
table_copy(v, dst[k])
else
dst[k] = v
end
end
end
local dst = {
}
table_copy(self, dst)
return dst
end
return module
如何使用模块 ⇓ \Downarrow ⇓
stack=require "stack"
print(stack.info)
push(stack,3)
push(stack,4)
push(stack,5)
push(stack,6)
showArray(stack)
print(pop(stack))
输出为
stack_2019_12_7@demllie
showArray>3 4 5 6
6
[Finished in 0.3s]
非递归的快速排序,其中的partition可以是双边循环、单边循环
stack=require "stack"
local function quicksort(arr,startIndex,endIndex)
local quicksort_stack=copy(stack)
--栈顶元素入栈
push(quicksort_stack,startIndex)
push(quicksort_stack,endIndex)
while isEmpty(quicksort_stack)==false do
--出栈得到起止下标
p2=pop(quicksort_stack)
p1=pop(quicksort_stack)
--得到基准元素位置
pivot=partition(arr,p1,p2)
--根据基准元素分成两部分
if p1<pivot-1 then
push(quicksort_stack,p1)
push(quicksort_stack,pivot-1)
end
if pivot+1<p2 then
push(quicksort_stack,pivot+1)
push(quicksort_stack,p2)
end
end
end
堆排序
--[[
lua做的数据结构堆heap.lua
]]
local module={
}
module.info='heap_2019_12_8@demllie'
--节点下沉
function downAdjust(self,parentIndex,len)
temp=self[parentIndex]
--以1作为开始的话,左孩子就是下面这样
childIndex=2*parentIndex
while childIndex<=len do
--如果有右孩子,且右孩子大于左孩子,就定位到右孩子
if childIndex+1 <= len and self[childIndex+1]>self[childIndex] then
childIndex=childIndex+1
end
--如果父节点大于任何一个孩子的值,就跳出
if temp >= self[childIndex] then
break
end
--无需真正交换,赋值即可
self[parentIndex]=self[childIndex]
parentIndex=childIndex
childIndex=2*parentIndex
end
self[parentIndex]=temp
end
function showArray(self)
io.write('showArray>')
for i=1,#self do
io.write(self[i])
--等距输出
if self[i]>9 then
io.write(' ')
else
io.write(' ')
end
end
print()
end
--堆排序
function heapSort(self)
--1,把无序数组变成最大堆
for i=(#self)/2,1,-1 do
downAdjust(self,i,#self)
end
showArray(self)
--2,循环删除堆顶元素,移动到尾部,调整堆产生新的堆顶
for i=#self,1,-1 do
temp=self[i]
self[i]=self[1]
self[1]=temp
downAdjust(self,1,i-1) --堆的长度在减少,意思是末尾的被排序好了的就不算在堆内了
end
end
return module
使用
require "heap"
print('hello lua developer\n堆排序')
arr={
1,8,9,10,3,2,6,7,4,5}
showArray(arr)
heapSort(arr)
showArray(arr)
输出为
hello lua developer
堆排序
showArray>1 8 9 10 3 2 6 7 4 5
showArray>10 8 9 7 5 2 6 1 4 3
showArray>1 2 3 4 5 6 7 8 9 10
[Finished in 0.2s]
参考:《漫画算法》