Python实现几种经典排序算法

1. 插入排序

基本思想:

每次外循环将遍历到的当前元素和左边的元素依次进行(内循环)数值比较,将其插入合适的位置。

class Solution(object):
    def InsertSort(self, nums):
        nums_length = len(nums)
        for i in range(nums_length):
            cur_val = nums[i] #取当前i索引的数值,暂存下来(后面可能会被覆盖)
            pre_index = i - 1 #取前一个元素的索引
            while pre_index >= 0 and cur_val < nums[pre_index]: #条件1:索引有效 and 条件2:当前元素数值<前一个元素
                #将前一个元素的数值赋给下一位,而非交换。相当于覆盖了下一位,不过由于事先保存了i位元素,所以无碍
                nums[pre_index+1] = nums[pre_index]
                #左移指针,继续比较之前的元素
                pre_index -= 1 
            nums[pre_index+1] = cur_val #将暂存的当前元素数值插入到合格位置
        return nums

2. 选择排序

基本思想:

一个数组可以看做由两部分构成,左边是已排序的,右边是未排序的;每一次的排序循环从未排序部分的第一个元素 x 开始,遍历整个未排序部分找到其中最小的元素,然后和 x 交换位置(效果等同于将这个当前最小元素加入已排序末位)。

class Solution(object):
    def SelectionSort(self, nums):
        nums_length = len(nums)
        for i in range(nums_length -1): # 因为最小/最大的元素在前len-1次中已经被交换了,剩下的一定是最大/最小的,排序结束
            min_index = i
            for k in range(i, nums_length): # 和 i 后面的所有元素做比较,每次的行程-1
                if nums[min_index] >= nums[k]:
                    min_index = k
            nums[min_index], nums[i] = nums[i], nums[min_index]
        return nums

3. 归并排序

基本思想:

分治法+合并子列。合并子列的意思是假定有两个分别已经排好顺序的子列,将其合并成一个有序的数列。分治法就是将数列不断二分,直到子列只有一个元素时,子列天然有序,然后一步步再慢慢合并上来,最后得到一个完整的有序数列。

class Solution(object):
    def MergeSort(self, nums):
        def merge(left, right): #合并子列
            result = []
            p_l = p_r = 0
            # 通过左右指针来实现对左右两个子列的检索
            # 某一子列到头说明另一子列剩下元素较大且有序,直接写进result就行
            while p_l < len(left) and p_r < len(right):
                if left[p_l] <= right[p_r]:
                    result.append(left[p_l])
                    p_l += 1 #左指针
                else:
                    result.append(right[p_r])
                    p_r += 1 #右指针
            #将左右子列剩下的元素写入result(某一子列已经到头,extend空List,不影响result)
            result.extend(left[p_l:len(left)])
            result.extend(right[p_r:len(right)])
            return result
        if len(nums) == 1:
            return nums
        mid = len(nums) // 2
        left = nums[:mid]
        right = nums[mid:]
        return merge(self.MergeSort(left), self.MergeSort(right))

4. 快速排序

基本思想:

分治+递归。

首先选定一个基准元素(比如数组中第一个元素),然后两个指针分别从左右对数组进行遍历,将大于基准元素的数放到较右边,将小于基准元素的数放到较左边(这一步的代码实现是关键)。当左右指针相遇时,循环结束,此时以相遇位置为界,左边全是小于基准的数,右边全是大于基准的数,将基准插入相遇位置,完成一次排序操作(这个操作可称为Partition,可以封装进函数),然后递归地对左边子列和右边子列进行同样的Partition操作。

class Solution(object):
    def QuickSort_Partition(self, nums, left, right):
        if left > right:
            return 
        pivot = nums[left] #取左1为基数
        index = 0 #基数的索引
        l = left
        r = right
        while l < r:
            while l < r and nums[r] >= pivot: #从右往左寻找小于基准pivot的数
                r -= 1
            nums[l] = nums[r] #将大于基准的数甩到左边
            while l < r and nums[l] < pivot: #从左往右寻找大于基准pivot的数
                l += 1 
            nums[r] = nums[l] #将小于基准的数甩到右边
        # left = right时,退出循环
        nums[r] = pivot #基准pivot已经被覆盖了,需要重新插入到循环退出的位置
        index = r #更新基准的索引        
        self.QuickSort_Partition(nums, left, index-1)
        self.QuickSort_Partition(nums, index+1, right)
        return nums

5. 堆排序

基本思想:

将数组看做一个完全二叉树从左至右、从上到下的一维展开式,将这个完全二叉树(也即这个数组)通过若干次heapify操作转化为大顶堆。此时根节点为二叉树中最大值,将其和最后一个节点交换,然后切掉这个最大值节点,再对根节点进行heapify操作将剩下的树恢复大顶堆性质。重复多次(每次都输出当前树中的最大值)直到排序完成。

关键:

  • 完全二叉树性质
  • 大顶堆与heapify操作
    • 大顶堆性质
    • heapify操作
    • 构建大顶堆
  • 使用堆进行排序
class Solution(object):
    # 堆排序
    def HeapSort(self, arr):
        
        # 建立大顶堆 (in-place):
        # 大顶堆性质:1.完全二叉树 2.任何一个父节点均大于其子节点(如果有)
        def build_heap(arr, length):
            last_parent_node = (length-2) // 2 #最后一个非叶节点
            i = last_parent_node
            while (i >= 0): #从右至左,从下往上heapify,构建大顶堆
                heapify(arr, i, length)
                i -= 1
        
        # heapify操作(in-place):
        # 比较父节点、左子节点、右子节点,将最大值推到父节点(即构建小型大顶堆)
        def heapify(arr, index, length):
            left = 2 * index + 1
            right = 2 * index + 2
            # 以父节点作为初始比较值
            max_index = index
            # 第一个条件是避免越界
            if left < length and arr[left] > arr[max_index]:
                max_index = left
            if right < length and arr[right] > arr[max_index]:
                max_index = right
            if max_index != index:
                swap(arr, max_index, index) # 将最大值推到父节点
                heapify(arr, max_index, length) # 子节点被换掉了,对子节点下方子树进行递归,保证子树的大顶堆性质
        
        # 按索引进行数组元素的交换 (in-place):
        def swap(arr, i, j):
            arr[i], arr[j] = arr[j], arr[i]
        
        # 执行堆排序
        length = len(arr) # 长度
        build_heap(arr, length) # 建立大顶堆
        k = length - 1 # 从最后一个节点开始
        while k >= 0:
            swap(arr, 0, k) # 将根节点(最大值)和最后一个节点交换
            k -= 1 #长度-1,相当于把最后一个节点(当前最大值)切掉(即后续不再对其操作)
            heapify(arr, 0, k) #heapify操作,确保新的根节点为最大值
        return arr
发布了5 篇原创文章 · 获赞 5 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/wyr_rise/article/details/100583932