前言
冒泡、冒泡改進、雞尾酒、快速……
話說,用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
如何使用模塊
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]
參考:《漫畫算法》