hack_hu 发表于 2018-12-16 21:24

【笔记】三种初级排序

# 三种初级排序 冒泡排序 选择排序 插入排序
![思维导图](https://img-blog.csdnimg.cn/20181216112052634.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM4Mjg4ODQ3,size_16,color_FFFFFF,t_70)
>此篇文章中展示的代码为 C 语言代码 ,数组索引操作替换为指针操作。
>
**排序算法可以分为两类 : 一种为原地排序,除了函数调用所需要的栈和固定数目的示例之外无需额外的内存的。另一类为其他排序方法,即非原地排序。**
## 思考 :原地排序在实际应用中的效果 。
通常数据在数据库中的存储除分类外是按照时间顺序生成的 ,例如一个商城系统的订单数据首先是按照订单生成的时间出现在数据库中的,当按照订单金额排序时 ,下单的时间也是一个很重要的参考数据 ,如果在排序时改变了其先后顺序 ,一方面做了无必要的内存消耗 ,另一方面改变了原始数据的排列 ,在下一次进行其他项目( 例如 :某用户所有订单按照金额和时间排序 )就要做更多的内存消耗。

## 排序的实质
**满序度** ,对于任意 N( 正整数 )个数排列的可能性有 n*(n-1)/2 种情况
**有序度** ,即数据中顺序正确的排列组合数
**无序度** ,即无序的排列组合数
**公式 :无序度 = 满序度 - 有序度**
==对于任意排序 ,其实质就是将其无序排序变成有序排列的过程 ,即增加数据的有序度 、减少无序度 ,当无序度为 0 时排序完成 。
重点 :当数据排序操作的次数越接近无序度时 , 说明该算法更好。==
## 冒泡排序
**定义** : 比较相邻两个数字的大小,满足条件时位置不变,不满足条件时交换其位置。
**属性** :原地排序,大小一致时数据位置不发生改变。在数组中 ,冒泡排序最好时间复杂度为 O(n) , 最坏时间复杂度为 O(n2) ,平均时间复杂度为 O(n)

```java
void bubbleSort(int *nums,int n)
{
        for (int i = 0; i < n-1; i++)
        {
                int flag = 0;
                for (int j = 0; j < n - 1-i;j++)
                {
//冒泡排序法 比较相邻的两个数如果满足条件(例如 : 大的在前,小的在后)则位置不变,不满足则交换二者的位置
                        if (*(nums + j) > *(nums + j + 1))
                        {
                                int temp =*(nums+j+1);
                                *(nums+j+1)= *(nums+j);
                                *(nums + j ) = temp;
                                flag = 1;
                        }
                }
                if (flag == 0)//当flag为 0 则说明未交换,则剩下的都是有序数列不用再循环
                        break;

        }
}
```

## 选择排序
**定义** :每次选择( 最大或最小 )的数字放在末尾。
**属性** :非原地排序,每次按顺序从后面插入。在数组中 ,选择排序最好时间复杂度为 O(n2) , 最坏时间复杂度为 O(n2) ,平均时间复杂度为 O(n2)

```java
void chooseSort(int *nums, int n)
{
        /*选择排序从头开始每次选择一个数为『 最大数 』
        ( 或最小数 )然后在剩下的数据中查找是否有大于(或小于它)的数
        如果有则交换两者的位置,没有则进行下一次排序*/

        for (int i = 0; i < n-1; i++)
        {
                for (int j = i+1; j < n; j++)
                {
                        if (*(nums + i) > *(nums + j))
                        {
                                int temp = *(nums + i);
                                *(nums + i) = *(nums + j);
                                *(nums + j) = temp;
                        }
                }
        }
}
```

## 插入排序
**定义** :将数据分成有序和无序两部分,将无序部分数据插入至有序数据中按顺序排列的位置,重复该过程直到无序部分数据为空。( 也满足了分治法的思想 )
**属性** :非原地排序 ,按大小插入到尾部 , 改变了数据原始的顺序 ,进行了额外的数据操作。在数组中 ,插入排序的最好时间复杂度为 O(n) ,最坏时间复杂度为 O(n2) ,平均时间复杂度为 O(n)

```java
void insertSort(int *nums, int n)
{
        //int *temps = (int *)malloc(sizeof(int )*n);
        /*
        把数据分成两个部分一部分是有序的另一部分是无序的,
        每次把无序数列中的第一个数插入到有序数列中按顺序排列的位置
        */
        int j=0, i=0;
        for (; i < n-1; i++)
        {
                int value = *(nums + i+1);
                for ( j = i; j>=0; )
                {
                        if (*(nums + j) > value)
                        {
                                *(nums + j + 1) = *(nums + j);
                                j--;
                        }
                        else
                                break;
                }
                *(nums + j+1) = value;
        }
}
```

## 思考在链表中三种排序时间复杂度有何变化
**如果是交换链表中结点的值 ,三种算法的复杂度无变化 。**

## 如果改变的是链表中结点的位置

冒泡排序复杂度不变 ,但因为链表结点位置交换的操作更为复杂 ,所以对性能的消耗更大。

选择排序复杂度不变 ,因为交换次数最多所以性能消耗更大。

插入排序插入数据时 ,数组数据后移操作变简单 ,相对来说性能消耗变小。

综合来看 ,插入排序在三种初级排序算法中有一定优势( 在链表等数据结构中算法的时间复杂度有优化 ,而其他两种的算法复杂度无优化 ) ,所以在实际使用中插入排序更为常用。

jimo 发表于 2018-12-16 21:39

链表的知识点能发一下吗?

cosxiaobaitu 发表于 2018-12-16 21:40

进来学习

ChinaF 发表于 2018-12-16 22:48

感谢分享

wangqiustc 发表于 2018-12-16 23:17

mark一下

mzhsohu 发表于 2018-12-17 01:00

感谢楼主的分享

20175101019 发表于 2018-12-17 07:47

加油哦,好好学吧

hack_hu 发表于 2018-12-17 14:57

jimo 发表于 2018-12-16 21:39
链表的知识点能发一下吗?

链表的帖子上上周发过
页: [1]
查看完整版本: 【笔记】三种初级排序