数据结构和算法 - 基础排序

目录

一、复杂度

1-1 空间复杂度 - 用于评估算法内存占用大小

1-2 时间复杂度 - 用来估计算法运行时间的

二、列表排序

2-1 基础排序

2-1-1 冒泡排序

2-1-2 选择排序

2-1-3 插入排序

2-2 高级排序

2-2-1 快速排序

2-2-2 堆排序

2-2-3 归并排序

2-3 非常用排序

2-3-1 希尔排序

2-3-2 基数排序

2-3-3 桶排序

2-4 常见排序总结

2-5 其他排序

2-5-1 计数排序 - 利用index顺序进行匹配计数

三、二分查找


一、复杂度

1-1 空间复杂度 - 用于评估算法内存占用大小

1-2 时间复杂度 - 用来估计算法运行时间的

一般来说,时间复杂度高的算法比复杂度低的算法慢。

循环减半的过程 → O(logn);循环n次,则复杂度为O(n^n)

  • 常见的时间复杂度(按效率排序)
    • O(1) < O(logn) < O(n) < O(nlogn) < O(n2) < O(n2logn) < O(n3)
  • 不常见的时间复杂度
    • O(n!) O(2n) O(nn) …

二、列表排序

2-1 基础排序

2-1-1 冒泡排序

排序原理:列表每两个相邻的数,如果前边的比后边的大,那么交换这两个数

def bubble_sort(li):
    # 10个数表示外层循环9次,每一次外层循环结束,表示一个最大数归为
    for i in range(len(li)-1):
        # 内层循环两两进行对比,循环总数-已循环次数
        for j in range(len(li)-1-i):
            if li[j] > li[j+1]:
            li[j], li[j+1] = li[j+1], li[j]
'''
value: 7 5 4 6 3 8 2 9 1
index: 0 1 2 3 4 5 6 7 8
时间复杂度:O(n^2)
空间复杂度:O(1)

内层循环:
当 i = 0
内层循环len(li)=1=i次
j[0] 和 j[1] 进行比较,若后者数大,则二者交换位置
j[1] j[2]
j[2] j[3]
    .
    .
    .
j[len(li)=2=i]  j[len(li)=1=i]
第一次循环结束,最大的数一定到了列表尾部
第二次外层循环从i=1,循环长度-1,即 len(li)-1-1
第三次外层循环从i=2,循环长度-1,即 len(li)-1-2
'''

基础冒泡排序优化 - 使用内置自定义flag

# 冒泡排序优化,使用flag变量判断是否为有序列表
# 若列表有序,只需要执行一次外层循环,当flag为false立马返回
# 当列表有序时,第一次循环都不会走入内层循环的判断语句内,则flag始终为False
def bubble_sort_0(li):
    for i in range(len(li)-1):
        flag = False
        for j in range(len(li)-i-1):
            if li[j] > li[j+1]:
                li[j], li[j+1] = li[j+1], li[j]
                flag = True
        if not flag:
        	return

2-1-2 选择排序

排序原理:取出最小值,每次内层循环将剩余值一一与最小值进行两两比对,进行是否重置最小值操作

def select_sort(li):
    # 10个数循环9次,每次外层循环结束,最小值归位
    for i in range(len(li) - 1):
    # 初始化最小值,第一次内层循环即将当前最小位的数当做最小值,min的索引值不动
        min_loc = i
        for j in range(i+1, len(li)):
            if li[j] < li[min_loc]:
                li[j], li[min_loc] = li[min_loc], li[j]
'''
value: 7 5 4 6 3 8 2 9 1
index: 0 1 2 3 4 5 6 7 8
时间复杂度:O(n^2)
空间复杂度:O(1)

内层循环:
当i=0 最小值为 li[0] = li[min]
内层从li[1]开始取到li[len(li)-1],即 range(1,len(li))
li[min] 依次和 li[j]进行对比,若li[j]小,则将li[min]和li[j]位置互换
仍然保持li[0]为最小值

'''

2-1-3 插入排序

排序原理:列表被分为有序区和无序区两个部分。最初有序区只有一个元素。每次从无序区选择一个元素,插入到有序区的位置,直到无序区变空。

def insert_sort(li):
    for i in range(1, len(li)):
        # tmp为取出的数
        tmp = li[i]
        # j为取出数的前一个索引值
        j = i - 1
        # 若取出的数前存在数,并且,取出的数小于前一个数,则将tmp的数覆盖为前一个数值
        # 比较索引向前走一位继续判断循环
        while j >= 0 and tmp < li[j]:
            li[j + 1] = li[j]
            j -= 1
        # 因为循环执行的是覆盖操作,则结束循环,一定有两个索引为值相同。修改前置的索引值。
        # 若结束循环后,j=-1,则将取出值置最前
        # 若结束循环后,j != -1,则将取出值放置在上一次循环比较前的位置,抵消j -= 1操作
        li[j + 1] = tmp
'''
value: 7 5 4 6 3 8 2 9 1
index: 0 1 2 3 4 5 6 7 8
时间复杂度:O(n^2)
空间复杂度:O(1)
'''

2-2 高级排序

2-2-1 快速排序

  • 取一个元素p(第一个元素),使元素p归位;
  • 列表被p分成两部分,左边都比p小,右边都比p大;
  • 递归完成排序。

def quick_sort(data, left, right):
    '''
    data - 数据列表
    left - 最左索引
    right - 最右索引

    quick(li, 0, len(li)-1)
    '''
    if left < right:
        mid = partition(data, left, right)
        # 迭代分左右部分归位p元素
        quick_sort(data, left, mid - 1)
        quick_sort(data, mid + 1, right)
        
def partition(data, left, right):
    '''
    归位取出的第一个元素
    data:列表
    left:最左索引
    right:最右索引
    '''
    # 提取出需要归位的元素
    tmp = data[left]
    # 当左索引和右索引未合并或者交换,则表示元素未归位
    while left < right:
        # 移动右指针,若右指针元素大于tmp,则指针继续移动
        while left < right and data[right] >= tmp:
            	right -= 1
        # 右指针元素小于tmp,跳出循环,将右指针值覆盖在左指针位
        data[left] = data[right]
        # 移动左指针,若左指针元素小于tmp,则指针继续移动
        while left < right and data[left] <= tmp:
            left += 1
        # 左指针元素大于tmp,跳出循环,将左指针值覆盖在右指针位置
       	data[right] = data[left]
    # 当左索引和右索引未合并或者交换,跳出指针移动循环,并将需归位元素赋予其位
    data[left] = tmp
    return left
'''
value: 7 5 4 6 3 8 2 9 1
index: 0 1 2 3 4 5 6 7 8
时间复杂度:O(nlogn) - partition函数为n,quick_sort为logn
空间复杂度:O(1)
'''

2-2-2 堆排序

2-2-3 归并排序

  • 分解:将列表越分越小,直至分成一个元素。一个元素即是有序的。
  • 合并:将两个有序列表归并,列表越来越大。
  • 一次归并:分解成单个元素后的比较。

合并过程演示 - merge函数
def merge(li, left, mid, right):
    '''
    li - 数据列表
    letf - 最左索引
    mid - 中间索引
    right - 最右索引
    '''
    i = left
    j = mid + 1
    # 新列表,用于将取出值有序排列
    ltmp = []
    # 取出最左元素和中间元素进行对比,将较小元素放入列表,对应指针右移
    while i <= mid and j <= right:
        if li[i] < li[j]:
            ltmp.append(li[i])
            i += 1
        else:
            ltmp.append(li[j])
            j += 1
    # 前一个循环结束,但左指针还未走完,全部循环添加(内部值一定有序)
    while i <= mid:
        ltmp.append(li[i])
        i += 1
    # 第一个循环结束,但右指针未走完,全部循环添加(内部值一定有序)
    while j <= right:
        ltmp.append(li[j])
        j += 1
    # 将新列表覆盖在原列表内,通过切片(取前不取后,因此右索引+1)
    li[left:right + 1] = ltmp



def merge_sort(li, left, right):
    if left < right:
        # 找到中间位置索引,取最大整除数
        mid = (left + right) // 2
        '''先迭代拆分,后迭代合并'''
        # 分解操作
        merge_sort(li, left, mid)
        merge_sort(li, mid + 1, right)
        # 合并操作
        merge(li, left, mid, right)
'''
value: 7 5 4 6 3 8 2 9 1
index: 0 1 2 3 4 5 6 7 8
时间复杂度:O(nlogn) 
空间复杂度:O(n)
'''

2-3 非常用排序

2-3-1 希尔排序

  • 希尔排序是一种分组插入排序算法。
  • 首先取一个整数d1=n/2,将元素分为d1个组,每组相邻量元素之间距离为d1,在各组内进行直接插入排序;
  • 取第二个整数d2=d1/2,重复上述分组排序过程,直到di=1,即所有元素在同一组内进行直接插入排序。
  • 希尔排序每趟并不使某些元素有序,而是使整体数据越来越接近有序;最后一趟排序使得所有数据有序。

def shell_sort(li):
	gap = len(li) // 2
    while gap > 0:
        for i in range(gap, len(li)):
            tmp = li[i]
            j = i - gap
            while j >= 0 and tmp < li[j]:
                li[j + gap] = li[j]
                j -= gap
            li[j + gap] = tmp
        gap /= 2	
'''
时间复杂度:O((1+τ)n) 或 O(1.3n)
'''

2-3-2 基数排序

2-3-3 桶排序

2-4 常见排序总结

2-5 其他排序

2-5-1 计数排序 - 利用index顺序进行匹配计数

  • vlaue: 10 9 8 7 9 6 2 4 5 2 9 1
  • index: 0 1 2 3 4 5 6 7 8 9 10
  • count: 0 1 2 0 1 1 1 1 1 3 1
  • list : 1 2 2 4 5 6 7 8 9 9 9 10
def count_sort(li):
    # 初始化一个全为0组成的11个数的列表,表示从0开始计数
    ltmp = [0 for i in range(11)]
    # 循环数据列表,value与index匹配,1对应li[1]内value值计数+1
    for x in li:
        ltmp[x] += 1
    # 清空原有数据列表
    li.clear()
    # 更具ltmp计数列表,循环列表内值次数,并将其索引值添加到原列表中。
    for index, val in enumerate(ltmp):
        for v in range(val):
            li.append(index)

三、二分查找

# 普通二分查找
def bin_search(data_set, value):
    '''
    在指定有序集合内查找索引
    :param data_set: 有序集合
    :param value: 查找值
    :return: 查找指定值的索引
    '''
    low = 0
    high = len(data_set) - 1
    while low <= high:
        mid = (low + high) // 2
        if data_set[mid] == value:
            return mid
        elif data_set[mid] > value:
            high = mid - 1
        else:
            low = mid + 1

# 基于迭代的二分查找
def bin_search_rec(data_set, value, low, high):
    '''
    指定范围你,对有序集合查找value的索引
    :param data_set: 有序集合
    :param value: 查找值
    :param low:最大值
    :param high:最小值
    :return:查找指定值的索引
    '''
    if low <= high:
        mid = (low + high) // 2
        if data_set[mid] == value:
            return mid
        elif data_set[mid] > value:
            return bin_search_rec(data_set, value, low, high)
        else:
            return bin_search_rec(data_set, value, low, high)
    else:
        return

 

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值