【lua】用LUA學習排序不會很奇怪嗎

前言

冒泡、冒泡改進、雞尾酒、快速……
話說,用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]

參考:《漫畫算法》

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章