数据结构八大算法详解

一、直接插入排序

  • 直接插入排序的核心思想就是:将数组中的所有元素依次跟前面已经排好的元素相比较,如果选择的元素比已排序的元素小,则交换,直到全部元素都比较过。
    因此,从上面的描述中我们可以发现,直接插入排序可以用两个循环完成:

  • 第一层循环:遍历待比较的所有数组元素
  • 第二层循环:将本轮选择的元素(selected)与已经排好序的元素(ordered)相比较。
    如果:selected > ordered,那么将二者交换
  • 代码实现
  • #直接插入排序
    def insert_sort(L):
        #遍历数组中的所有元素,其中0号索引元素默认已排序,因此从1开始
        for x in range(1,len(L)):
        #将该元素与已排序好的前序数组依次比较,如果该元素小,则交换
        #range(x-1,-1,-1):从x-1倒序循环到0
            for i in range(x-1,-1,-1):
        #判断:如果符合条件则交换
                if L[i] > L[i+1]:
                    temp = L[i+1]
                    L[i+1] = L[i]
                    L[i] = temp

    二、希尔排序

  • 希尔排序的算法思想:将待排序数组按照步长gap进行分组,然后将每组的元素利用直接插入排序的方法进行排序;每次将gap折半减小,循环上述操作;当gap=1时,利用直接插入,完成排序。
    同样的:从上面的描述中我们可以发现:希尔排序的总体实现应该由三个循环完成:

  • 第一层循环:将gap依次折半,对序列进行分组,直到gap=1
  • 第二、三层循环:也即直接插入排序所需要的两次循环。具体描述见上。
  • 代码实现:
  • #希尔排序
    def insert_shell(L):
        #初始化gap值,此处利用序列长度的一般为其赋值
        gap = (int)(len(L)/2)
        #第一层循环:依次改变gap值对列表进行分组
        while (gap >= 1):
        #下面:利用直接插入排序的思想对分组数据进行排序
        #range(gap,len(L)):从gap开始
            for x in range(gap,len(L)):
        #range(x-gap,-1,-gap):从x-gap开始与选定元素开始倒序比较,每个比较元素之间间隔gap
                for i in range(x-gap,-1,-gap):
        #如果该组当中两个元素满足交换条件,则进行交换
                    if L[i] > L[i+gap]:
                        temp = L[i+gap]
                        L[i+gap] = L[i]
                        L[i] =temp
        #while循环条件折半
            gap = (int)(gap/2)

    三、简单选择排序

  • 简单选择排序的基本思想:比较+交换。

  • 从待排序序列中,找到关键字最小的元素;
  • 如果最小元素不是待排序序列的第一个元素,将其和第一个元素互换;
  • 从余下的 N - 1 个元素中,找出关键字最小的元素,重复(1)、(2)步,直到排序结束。
    因此我们可以发现,简单选择排序也是通过两层循环实现。
    第一层循环:依次遍历序列当中的每一个元素
    第二层循环:将遍历得到的当前元素依次与余下的元素进行比较,符合最小元素的条件,则交换。
  • 代码实现
  • # 简单选择排序
    def select_sort(L):
    #依次遍历序列中的每一个元素
        for x in range(0,len(L)):
    #将当前位置的元素定义此轮循环当中的最小值
            minimum = L[x]
    #将该元素与剩下的元素依次比较寻找最小元素
            for i in range(x+1,len(L)):
                if L[i] < minimum:
                    temp = L[i];
                    L[i] = minimum;
                    minimum = temp
    #将比较后得到的真正的最小值赋值给当前位置
            L[x] = minimum

    四、堆排序

  • 堆的概念
    堆:本质是一种数组对象。特别重要的一点性质:<b>任意的叶子节点小于(或大于)它所有的父节点</b>。对此,又分为大顶堆和小顶堆,大顶堆要求节点的元素都要大于其孩子,小顶堆要求节点元素都小于其左右孩子,两者对左右孩子的大小关系不做任何要求。
    利用堆排序,就是基于大顶堆或者小顶堆的一种排序方法。下面,我们通过大顶堆来实现。

  • 基本思想:
    堆排序可以按照以下步骤来完成:首先将序列构建称为大顶堆;(这样满足了大顶堆那条性质:位于根节点的元素一定是当前序列的最大值) 

  • 取出当前大顶堆的根节点,将其与序列末尾元素进行交换;
    (此时:序列末尾的元素为已排序的最大值;由于交换了元素,当前位于根节点的堆并不一定满足大顶堆的性质)
  • 对交换后的n-1个序列元素进行调整,使其满足大顶堆的性质;

  • 重复2.3步骤,直至堆中只有1个元素为止

  • 代码实现:

  • #-------------------------堆排序--------------------------------
    #**********获取左右叶子节点**********
    def LEFT(i):
        return 2*i + 1
    def RIGHT(i):
        return 2*i + 2
    #********** 调整大顶堆 **********
    #L:待调整序列 length: 序列长度 i:需要调整的结点
    def adjust_max_heap(L,length,i):
    #定义一个int值保存当前序列最大值的下标
        largest = i
    #执行循环操作:两个任务:1 寻找最大值的下标;2.最大值与父节点交换
        while (1):
    #获得序列左右叶子节点的下标
            left,right = LEFT(i),RIGHT(i)
    #当左叶子节点的下标小于序列长度 并且 左叶子节点的值大于父节点时,将左叶子节点的下标赋值给largest
            if (left < length) and (L[left] > L[i]):
                largest = left
                print('左叶子节点')
            else:
                largest = i
    #当右叶子节点的下标小于序列长度 并且 右叶子节点的值大于父节点时,将右叶子节点的下标值赋值给largest
            if (right < length) and (L[right] > L[largest]):
                largest = right
                print('右叶子节点')
    #如果largest不等于i 说明当前的父节点不是最大值,需要交换值
            if (largest != i):
                temp = L[i]
                L[i] = L[largest]
                L[largest] = temp
                i = largest
                print(largest)
                continue
            else:
                break
    #********** 建立大顶堆 **********
    def build_max_heap(L):
        length = len(L)
        for x in range((int)((length-1)/2),-1,-1):
            adjust_max_heap(L,length,x)
    #********** 堆排序 **********
    def heap_sort(L):
    #先建立大顶堆,保证最大值位于根节点;并且父节点的值大于叶子结点
        build_max_heap(L)
    #i:当前堆中序列的长度.初始化为序列的长度
        i = len(L)
    #执行循环:1. 每次取出堆顶元素置于序列的最后(len-1,len-2,len-3...)
    #         2. 调整堆,使其继续满足大顶堆的性质,注意实时修改堆中序列的长度
        while (i > 0):
            temp = L[i-1]
            L[i-1] = L[0]
            L[0] = temp
    #堆中序列长度减1
            i = i-1
    #调整大顶堆
            adjust_max_heap(L,i,0)

    五、冒泡排序

  • 冒泡排序思路比较简单:

  • 将序列当中的左右元素,依次比较,保证右边的元素始终大于左边的元素;
    ( 第一轮结束后,序列最后一个元素一定是当前序列的最大值;)
  • 对序列当中剩下的n-1个元素再次执行步骤1。
  • #冒泡排序
    def bubble_sort(L):
        length = len(L)
    #序列长度为length,需要执行length-1轮交换
        for x in range(1,length):
    #对于每一轮交换,都将序列当中的左右元素进行比较
    #每轮交换当中,由于序列最后的元素一定是最大的,因此每轮循环到序列未排序的位置即可
            for i in range(0,length-x):
                if L[i] > L[i+1]:
                    temp = L[i]
                    L[i] = L[i+1]
                    L[i+1] = temp

    六、快速排序

  • 本思想:挖坑填数+分治法
  • 从序列当中选择一个基准数(pivot)
    在这里我们选择序列当中第一个数最为基准数
  • 将序列当中的所有数依次遍历,比基准数大的位于其右侧,比基准数小的位于其左侧
  • 重复步骤1.2,直到所有子集当中只有一个元素为止。
    用伪代码描述如下:
    1.i =L; j = R; 将基准数挖出形成第一个坑a[i]。
    2.j--由后向前找比它小的数,找到后挖出此数填前一个坑a[i]中。
    3.i++由前向后找比它大的数,找到后也挖出此数填到前一个坑a[j]中。
    4.再重复执行2,3二步,直到i==j,将基准数填入a[i]中
  • 代码实现:
  • #快速排序
    #L:待排序的序列;start排序的开始index,end序列末尾的index
    #对于长度为length的序列:start = 0;end = length-1
    def quick_sort(L,start,end):
        if start < end:
            i , j , pivot = start , end , L[start]
            while i < j:
    #从右开始向左寻找第一个小于pivot的值
                while (i < j) and (L[j] >= pivot):
                    j = j-1
    #将小于pivot的值移到左边
                if (i < j):
                    L[i] = L[j]
                    i = i+1 
    #从左开始向右寻找第一个大于pivot的值
                while (i < j) and (L[i] < pivot):
                    i = i+1
    #将大于pivot的值移到右边
                if (i < j):
                    L[j] = L[i]
                    j = j-1
    #循环结束后,说明 i=j,此时左边的值全都小于pivot,右边的值全都大于pivot
    #pivot的位置移动正确,那么此时只需对左右两侧的序列调用此函数进一步排序即可
    #递归调用函数:依次对左侧序列:从0 ~ i-1//右侧序列:从i+1 ~ end
            L[i] = pivot
    #左侧序列继续排序
            quick_sort(L,start,i-1)
    #右侧序列继续排序
            quick_sort(L,i+1,end)

    七、归并排序

  • 归并排序是建立在归并操作上的一种有效的排序算法,该算法是采用分治法的一个典型的应用。它的基本操作是:将已有的子序列合并,达到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。
  • 归并排序其实要做两件事:
  • 分解----将序列每次折半拆分
  • 合并----将划分后的序列段两两排序合并
    因此,归并排序实际上就是两个操作,拆分+合并
  • 如何合并?
    L[first...mid]为第一段,L[mid+1...last]为第二段,并且两端已经有序,现在我们要将两端合成达到L[first...last]并且也有序。
  • 首先依次从第一段与第二段中取出元素比较,将较小的元素赋值给temp[]
  • 重复执行上一步,当某一段赋值结束,则将另一段剩下的元素赋值给temp[]
  • 此时将temp[]中的元素复制给L[],则得到的L[first...last]有序
  • 如何分解?
    在这里,我们采用递归的方法,首先将待排序列分成A,B两组;然后重复对A、B序列
    分组;直到分组后组内只有一个元素,此时我们认为组内所有元素有序,则分组结束。
  • 代码实现

  • # 归并排序
    #这是合并的函数
    # 将序列L[first...mid]与序列L[mid+1...last]进行合并
    def mergearray(L,first,mid,last,temp):
    #对i,j,k分别进行赋值
        i,j,k = first,mid+1,0
    #当左右两边都有数时进行比较,取较小的数
        while (i <= mid) and (j <= last):
            if L[i] <= L[j]:
                temp[k] = L[i]
                i = i+1
                k = k+1
            else:
                temp[k] = L[j]
                j = j+1
                k = k+1
    #如果左边序列还有数
        while (i <= mid):
            temp[k] = L[i]
            i = i+1
            k = k+1
    #如果右边序列还有数
        while (j <= last):
            temp[k] = L[j]
            j = j+1
            k = k+1
    #将temp当中该段有序元素赋值给L待排序列使之部分有序
        for x in range(0,k):
            L[first+x] = temp[x]
    # 这是分组的函数
    def merge_sort(L,first,last,temp):
        if first < last:
            mid = (int)((first + last) / 2)
    #使左边序列有序
            merge_sort(L,first,mid,temp)
    #使右边序列有序
            merge_sort(L,mid+1,last,temp)
    #将两个有序序列合并
            mergearray(L,first,mid,last,temp)
    # 归并排序的函数
    def merge_sort_array(L):
    #声明一个长度为len(L)的空列表
        temp = len(L)*[None]
    #调用归并排序
        merge_sort(L,0,len(L)-1,temp)

    八、基数排序

  • 基数排序:通过序列中各个元素的值,对排序的N个元素进行若干趟的“分配”与“收集”来实现排序。
    分配:我们将L[i]中的元素取出,首先确定其个位上的数字,根据该数字分配到与之序号相同的桶中
    收集:当序列中所有的元素都分配到对应的桶中,再按照顺序依次将桶中的元素收集形成新的一个待排序列L[ ]
    对新形成的序列L[]重复执行分配和收集元素中的十位、百位...直到分配完该序列中的最高位,则排序结束
  • 根据上述“基数排序”的展示,我们可以清楚的看到整个实现的过程
  • 代码实现
  • #************************基数排序****************************
    #确定排序的次数
    #排序的顺序跟序列中最大数的位数相关
    def radix_sort_nums(L):
        maxNum = L[0]
    #寻找序列中的最大数
        for x in L:
            if maxNum < x:
                maxNum = x
    #确定序列中的最大元素的位数
        times = 0
        while (maxNum > 0):
            maxNum = (int)(maxNum/10)
            times = times+1
        return times
    #找到num从低到高第pos位的数据
    def get_num_pos(num,pos):
        return ((int)(num/(10**(pos-1))))%10
    #基数排序
    def radix_sort(L):
        count = 10*[None]       #存放各个桶的数据统计个数
        bucket = len(L)*[None]  #暂时存放排序结果
    #从低位到高位依次执行循环
        for pos in range(1,radix_sort_nums(L)+1):
            #置空各个桶的数据统计
            for x in range(0,10):
                count[x] = 0
            #统计当前该位(个位,十位,百位....)的元素数目
            for x in range(0,len(L)):
                #统计各个桶将要装进去的元素个数
                j = get_num_pos(int(L[x]),pos)
                count[j] = count[j]+1
            #count[i]表示第i个桶的右边界索引
            for x in range(1,10):
                count[x] = count[x] + count[x-1]
            #将数据依次装入桶中
            for x in range(len(L)-1,-1,-1):
                #求出元素第K位的数字
                j = get_num_pos(L[x],pos)
                #放入对应的桶中,count[j]-1是第j个桶的右边界索引
                bucket[count[j]-1] = L[x]
                #对应桶的装入数据索引-1
                count[j] = count[j]-1
            # 将已分配好的桶中数据再倒出来,此时已是对应当前位数有序的表
            for x in range(0,len(L)):
                L[x] = bucket[x]

    原文:https://www.cnblogs.com/hokky/p/8529042.html

12篇学通csharp网络编程——第四篇 TCP应用编程 12篇学通csharp网络编程——第三篇 HTTP应用编程(下) 12篇学通csharp网络编程——第二篇 HTTP应用编程(上) 12篇学通csharp网络编程——第一篇 基础之进程线程 Lucene(1)lucene,你也会(7篇)——第一篇 快速入门 MongoDB(8)8天学通MongoDB——第八天 驱动实践 8天学通MongoDB——第七天 运维技术 8天学通MongoDB——第六天 分片技术 8天学通MongoDB——第五天 主从复制 8天学通MongoDB——第四天 索引操作 8天学通MongoDB——第三天 细说高级操作 8天学通MongoDB——第二天 细说增删查改 8天学通MongoDB——第一天 基础入门 UML系列(4)团队沟通利器之UML——类图 团队沟通利器之UML—— 序列图 团队沟通利器之UML——用例图 团队沟通利器之UML——活动图 wcf系列(5)wcf系列学习5天速成——第五天 服务托管 wcf系列学习5天速成——第四天 wcf之分布式架构 wcf系列学习5天速成——第三天 事务的使用 wcf系列5天速成——第二天 binding的使用(2) wcf系列5天速成——第一天 binding的使用(1) wpf系列(8)8天入门wpf—— 第八天 最后的补充 8天入门wpf—— 第七天 画刷 8天入门wpf—— 第六天 细说控件 8天入门wpf—— 第五天 数据绑定 8天入门wpf—— 第四天 模板 8天入门wpf—— 第三天 样式 8天入门wpf—— 第二天 xaml详解 8天入门wpf—— 第一天 基础概念介绍 并行开发(8)8天玩转并行开发——第八天 用VS性能向导解剖你的程序 8天玩转并行开发——第七天 简要分析任务与线程池 8天玩转并行开发——第六天 异步编程模型 8天玩转并行开发——第五天 同步机制(下) 8天玩转并行开发——第四天 同步机制(上) 8天玩转并行开发——第三天 plinq的使用 8天玩转并行开发——第二天 Task的使用 8天玩转并行开发——第一天 Parallel的使用 多线程系列(5)5天不再惧怕多线程——第五天 线程池 5天不再惧怕多线程——第四天 信号量 5天不再惧怕多线程——第三天 互斥体 5天不再惧怕多线程——第二天 锁机制 5天不再惧怕多线程——第一天 尝试Thread 经典算法专题(21)经典算法题每日演练——第二十一题 十字链表 经典算法题每日演练——第二十题 三元组 经典算法题每日演练——第十九题 双端队列 经典算法题每日演练——第十八题 外排序 经典算法题每日演练——第十七题 Dijkstra算法 经典算法题每日演练——第十六题 Kruskal算法 经典算法题每日演练——第十五题 并查集 经典算法题每日演练——第十四题 Prim算法 经典算法题每日演练——第十三题 赫夫曼树 经典算法题每日演练——第十二题 线段树 经典算法题每日演练——第十一题 Bitmap算法 经典算法题每日演练——第十题 树状数组 经典算法题每日演练——第九题 优先队列 经典算法题每日演练——第八题 AC自动机 经典算法题每日演练——第七题 KMP算法 经典算法题每日演练——第六题 协同推荐SlopeOne 算法 经典算法题每日演练——第五题 字符串相似度 经典算法题每日演练——第四题 最长公共子序列 经典算法题每日演练——第三题 猴子吃桃 经典算法题每日演练——第二题 五家共井 经典算法题每日演练——第一题 百钱买百鸡 开发利器系列(1)介绍一个小工具 Linqer 那点所谓的分布式(2)那点所谓的分布式——memcache 那点所谓的分布式——redis 树结构专题(5)6天通吃树结构—— 第五天 Trie树 6天通吃树结构—— 第四天 伸展树 6天通吃树结构—— 第三天 Treap树 6天通吃树结构—— 第二天 平衡二叉树 6天通吃树结构—— 第一天 二叉查找树 算法速成系列(15)算法系列15天速成——第十五天 图【下】(大结局) 算法系列15天速成——第十四天 图【上】 算法系列15天速成——第十三天 树操作【下】 算法系列15天速成——第十二天 树操作【中】 算法系列15天速成——第十一天 树操作(上) 算法系列15天速成——第十天 栈 算法系列15天速成——第九天 队列 算法系列15天速成——第八天 线性表【下】 算法系列15天速成——第七天 线性表【上】 算法系列15天速成——第六天 五大经典查找【下】 算法系列15天速成——第五天 五大经典查找【中】 算法系列15天速成——第四天 五大经典查找【上】 算法系列15天速成——第三天 七大经典排序【下】 算法系列15天速成——第二天 七大经典排序【中】 算法系列15天速成——第一天 七大经典排序【上】 算法洗脑系列(8)算法洗脑系列(8篇)——第八篇 概率思想 算法洗脑系列(8篇)——第七篇 动态规划 算法洗脑系列(8篇)——第六篇 回溯思想 算法洗脑系列(8篇)——第五篇 分治思想 算法洗脑系列(8篇)——第四篇 枚举思想 算法洗脑系列(8篇)——第三篇 贪心思想 算法洗脑系列(8篇)——第二篇 递归思想 算法洗脑系列(8篇)——第一篇 递推思想 天籁数学(3)天籁数学——数列篇(3) 天籁数学——数列篇(2) 天籁数学——数列篇(1) 图形图像(1)玩玩图形图像——第一篇:图片灰度化 小爬虫系列(4)玩玩小爬虫——抓取时的几个小细节 玩玩小爬虫——抓取动态页面 玩玩小爬虫——试搭小架构 玩玩小爬虫——入门
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

卫龙~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值