排序

通俗的定义:就是重新排列 表/集合 中的 元素/数据元素/记录, 使 表/集合 中的元素 满足按其关键字 递增或递减的过程。

严格定义:

输入:n个记录R1 R2 ......Rn, 对应的关键字为 K1 K2 ......Kn.

输出:输入序列的一个重排: R1’ R2’......Rn’, 使得对应的关键字满足 K1’ <= k2’ <= ...... <=Kn’

例子:对所有学生按 总分进行排序,如果总分相等,再按 语数外总分进行排序。

从这个例子中看出,多个关键字的排序 最终都可以 转化为 单个关键字的 排序,因此下面的讨论的是 单个关键字的 排序。

几个特征

排序的稳定性/排序算法的稳定性:

定义:Ki = kj ( 1<= i <= n, 1<= j <= n, i≠j),  在排序前 Ri 在Rj 前面 (即 i <j)。 如果排序后Ri 仍然在 Rj 前面, 则称所用的 排序方法 是稳定的; 反之,如果可能使 排序后的 Rj 在Ri 前面,则称 所有的排序算法 是 不稳定的。

内排序 与 外排序

根据在排序的过程中,待排序的记录 是否全部 被放置在内存中,排序分为:内排序和 外排序。

对于内排序来说 影响其 时间性能/时间复杂度 的是:比较次数 和 移动次数

0 最普通双层for循环排序

c语言版本代码如下:

/* 最普通的排序 双for循环排序/冒泡排序初级版 */
void bubbleSort0(int a[], int length)
{int i,j,temp;for( i = 0; i <= length - 2; i++){for( j = i+1; j <= length - 1; j++){if(a[i] > a[j]){temp = a[i];a[i] = a[j];a[j] = temp;}}}
}
/*
缺陷: 排序完1, 2的位置后,对其余的关键字的排序没什么帮助(数字3反而被换到了最后一位)。*/

php语言 版本的代码如下:

function bubbleSort0(array $arr)
{$length = count($arr);for($i = 0; $i <= $length - 2 ; $i++){for($j = $i+1;  $j <= $length - 1; $j++){if($arr[$i] > $arr[$j]){$tmp = $arr[$i];$arr[$i] = $arr[$j];$arr[$j] = $tmp;}}}return $arr;
}$arr = [9, 1, 5, 8, 3, 7, 4, 6, 2];$newArr = bubbleSort0($arr);
print_r($newArr);

i= 0 ,i = 1 排序过程(比较和交换) 如下图

图的说明:

观察后发现,在排序好 index=0 ,1 的位置后,对其余关键字的排序没有什么帮助(数字3反而被换到了最后一位)。

1 冒泡排序 Bubble Sort

定义: 从底部开始 两两比较相邻记录的关键字,如果反序则交换,选出本轮最小的,(第一轮选出最小的那个放到 0 索引位,第二轮选出倒数第二小的放到 1索引位....依次)。这样的过程要循环n-1次(即外层循环要n-1次)。

假设待排序的关键字序列是{9,1,5,8,3,7,4,6,2}

i=0; (即第一趟)时,排序的过程 (比较 和交换)如下图:

图的说明:

1. 每一列代表 数组的一个状态, 中间的 箭头表示一个操作(包含 比较+交换);图中有 9个状态,8个操作。

2. 如果把 两两比较 画作 一个圆圈(就像气泡),那么这种排序方法 在视觉上 就和 冒泡 有共同的特征了。

c语言版本代码如下:

#include <stdio.h>
#include <stdlib.h>/* 冒泡排序
* 每次内循环 都从最后两个的比较开始;,内循环一次就会产生一 小值 浮到最上边。
* @param a  数组
* @param length  数组大小
*/
void bubbleSort(int a[], int length)
{int i,j,temp;for(i = 0; i < length - 1; i++){for(j= length-2; j >= i; j--)  //注意 j 是从后往前循环{if(a[j] > a[j+1])  // 如果 前者 大于 紧挨着的 后者{temp = a[j];a[j] = a[j+1];a[j+1] = temp;}}}
}#define N 9
int main()
{int i;int arr[N] = {9,1,5,8,3,7,4,6,2};printf("排序前:");for(i = 0; i < N-1; i++)printf("%d,", arr[i]);printf("%d", arr[i]);printf("\n");bubbleSort(arr, N);printf("排序后:");for(i = 0; i < N-1; i++)printf("%d,", arr[i]);printf("%d", arr[i]);printf("\n");
}

php版本的代码如下:

function bubbleSort(array $arr)
{$length = count($arr);for($i = 0; $i < $length - 1; $i++){for($j = $length - 2; $j >= $i; $j--){if($arr[$j] >  $arr[$j + 1]){$tmp = $arr[$j];$arr[$j] = $arr[$j+1];$arr[$j + 1] = $tmp;}}}return $arr;
}$arr = [9, 1, 5, 8, 3, 7, 4, 6, 2];$newArr = bubbleSort($arr);
print_r($newArr);

1. 1 冒泡排序的优化

如果待排序的序列是{2,1,3,4,5,6,7,8,9} ,也就是说,除了 index = 0, 1 的关键字需要交换外,别的都已经是正常的顺序。

但是前面的冒泡算法还是 i=1, 2 ...7  这么 一趟趟的 比较和交换。

方案:增加一个标记flag  值 为 true 就是需要外层循环,false 就不需要外层循环, 同时 true 就是 发生了交换

#include <stdio.h>
#include <stdlib.h>void bubbleSortWithFlag(int a[], int length)
{int i,j,temp;int flag = 1;for(i = 0; i < length - 1 && flag; i++){printf("第%d 趟开始:\n", (i+1));flag = 0;for(j= length-2; j >= i; j--)  //注意 j 是从后往前循环{if(a[j] > a[j+1])  // 如果 前者 大于 紧挨着的 后者{temp = a[j];a[j] = a[j+1];a[j+1] = temp;flag = 1;}}}
}#define N 9
int main()
{int i;//int arr[N] = {9,1,5,8,3,7,4,6,2};int arr[N] = {2,1,3,4,5,6,7,8,9};//int arr[N] = {3,1};printf("排序前:");for(i = 0; i < N-1; i++)printf("%d,", arr[i]);printf("%d", arr[i]);printf("\n");bubbleSortWithFlag(arr, N);printf("排序后:");for(i = 0; i < N-1; i++)printf("%d,", arr[i]);printf("%d", arr[i]);printf("\n");
}[xingqiji@localhost sort]$ ./a.out
排序前:2,1,3,4,5,6,7,8,9
第1 趟开始:
第2 趟开始:
排序后:1,2,3,4,5,6,7,8,9
[xingqiji@localhost sort]$ 

php版本代码:

function bubbleSortWithFlag($arr)
{$length = count($arr);$flag = true;for($i = 0; $i < $length - 1 && $flag; $i++){printf("第%d 趟开始:\n", ($i+1));$flag = false;for($j = $length -2; $j >= $i; $j--){if($arr[$j] > $arr[$j+1]){$tmp = $arr[$j];$arr[$j] = $arr[$j+1];$arr[$j+1] = $tmp;$flag = true;}}}return $arr;
}$arr = [2, 1, 3, 4, 5, 6, 7, 8, 9];$newArr = bubbleSortWithFlag($arr);
print_r($newArr);

1.2 冒泡算法的时间复杂度

普通冒泡排序:

改进后的冒泡排序:

2.  简单选择排序 (Simple Selection Sort)

就是通过 n- (i+1) 次 关键字 之间的比较,从 n - i 个记录中 选出 关键字最小的记录 ,并和 第  i  (0 <= i <= n-2 )个 记录 交换 (i :是索引的含义,  i+1 可以是趟含义 如 第 i+1 趟)

C语言代码如下:

#include <stdio.h>
#include <stdlib.h>/*
对顺序表L 简单选择排序
*/
void SelectSort(int a[], int length)
{int i,j,min , temp;for(i=0; i < length-1; i++){min = i;                //将当前下标定义为最小值for(j = i+1; j < length; j++){if(a[min] > a[j])min = j;}if(i!=min){             //如果 min 不等于 i,说明找到最小值,交换// 交换a[i] 与 a[min] 的值。temp = a[i];a[i] = a[min];a[min] = temp;}}
}#define N 9
int main()
{int i;int arr[N] = {9,1,5,8,3,7,4,6,2};printf("排序前:");for(i = 0; i < N-1; i++)printf("%d,", arr[i]);printf("%d", arr[i]);printf("\n");SelectSort(arr, N);printf("排序后:");for(i = 0; i < N-1; i++)printf("%d,", arr[i]);printf("%d", arr[i]);printf("\n");
}

php版本代码如下:

function selectSort(array $arr)
{$length = count($arr);for($i = 0; $i < $length - 1; $i++){$min = $i;  //将当前下标定义为为 最小值for($j = $i + 1; $j < $length; $j++){if($arr[$min] > $arr[$j]){$min = $j;}}if($i != $min) {    // 说明找到了最小的值,交换$tmp = $arr[$i];$arr[$i] = $arr[$min];$arr[$min] = $tmp;}}return $arr;
}$arr = [9, 1, 5, 8, 3, 7, 4, 6, 2];$newArr = selectSort($arr);
print_r($newArr);

描述:

第一趟 :

i=0;   第1 趟(i+1);   a[i] 即 a[0] 是 9  ; min = 0;

j = i+1 即 j=1 ; a[j]  和 a[i] 即 a[min] 比较,a[j] 小于 a[min]  ,则 min= j ;  j++ 继续 比较 a[j] 与 a[min]  ......  最终 min=1;

共比较了 n- (i+1) 次

min != i  ,所以交换 a[min]  与 a[i];

共需要 n-1 趟

2.2  简单选择 排序 复杂度

比较次数 :  (n-1)  +  (n-2)   + ... + 1 =  n(n-1)/2    ;  时间复杂度 O(n^2)

交换次数: 最好的时候 0次 , 最差的时候(一开始降序时),交换次数为 n-1 次;  时间复杂度:O(n)

最终的 排序时间复杂度是  比较与交换次数总和:O(n^2)

3. 直接插入排序 (Straight Insertion Sort)

将一个记录插入到已经排好序的有序表中, 从而得到一个新的,记录数增1的 有序表。

C语言代码如下:

#include <stdio.h>
#include <stdlib.h>/*** 插入排序*/
void InsertSort(int a[], int length)
{int i,j;for(i=1; i < length; i++)//循环从第2个元素开始{if(a[i]<a[i-1]){int temp=a[i];a[i] = a[i-1];    //将i-1 对应的 值 后移一位for(j=i-2; j>=0 && a[j]>temp; j--){a[j+1]=a[j];  // 将j 对应的 值 后移一位}a[j+1]=temp;//此处就是a[j+1]=temp;}}
}#define N 9
int main()
{int i;int arr[N] = {9,1,5,8,3,7,4,6,2};printf("排序前:");for(i = 0; i < N-1; i++)printf("%d,", arr[i]);printf("%d", arr[i]);printf("\n");InsertSort(arr, N);printf("排序后:");for(i = 0; i < N-1; i++)printf("%d,", arr[i]);printf("%d", arr[i]);printf("\n");
}

描述:

从第二个元素开始:i=1; 比较与 前一个元素 i-1 ,如果 比前一个小 ,则 :

前一个元素后移;

继续 比较 i即 temp 与 前前元素j ,如果 比前前元素 j 还小,则 j 对应的元素 后移一位

......

将 最后一个 小于 temp 的元素对应的 位置上 用 temp 赋值上。

php语言代码如下:

function insertSort($arr)
{$length = count($arr);for($i=1;  $i < $length; $i++){if($arr[$i] < $arr[$i-1]){$tmp = $arr[$i];$arr[$i] = $arr[$i-1];    //将 i-1 对应的值  后移一位for( $j = $i-2;  $j >= 0 && $arr[$j] > $tmp ; $j--){$arr[$j+1] = $arr[$j];   //将 j 对应的值 后移一位}$arr[$j+1] = $tmp;  // 一开始的 $i 对应的值 即 $tmp  就应该放在 $j+1 下标对应的空间中}}return $arr;
}

3.2 直接插入排序 的时间复杂度

最好的情况:例如:{2,3,4,5,6}

比较次数是 :n-1 次

最坏的情况:例如:{6,5,4,3,2}

比较的次数: 1 + 2  + ... + (n-1) =  n(n-1)/ 2   ;

移动的次数 / 赋值的次数(准确的说) : 3 + 4 + ... +  (n+1) =  (n+4)(n-1)/2 ;

总的来说时间复杂度: O(n^2)

4. 希尔排序 (可以看做 是直接插入排序 的改进)

对待排序的 集合 进行 分组,分组方式是:将相距某个 “增量”的 记录 组成 一个子序列, 这样才能保证在子序列内分别进行直接插入排序后得到的 结果是 基本有序 而不是 局部有序。

C语言代码如下:

#include <stdio.h>
#include <stdlib.h>// 声明打印函数
void printArray(int a[], int length);/*** 希尔排序*/
void ShellSort(int a[], int length)
{int i,j,k=0, temp;int increment = length;do{increment = increment/3 + 1;for(i=increment; i< length; i++)   // 注意是依次轮流每个组的一个元素来 进行插入排序;而不是把一个组完全排好,再排其他组。{if(a[i] < a[i-increment])  // 若成立:需要将a[i] 插入有序增量子表{temp = a[i];a[i] = a[i-increment]; // 将 i-increment对应的值 后移一位for(j= i- 2 * increment; j >= 0 && temp < a[j]; j-=increment)a[j+increment] = a[j]; // 记录后移a[j+increment] = temp;     //插入}}printf(" 第%d 次分组排序结果:", ++k);printArray(a, length);}while(increment > 1);
}/***  打印数组*/
void printArray(int a[], int length)
{int i;for(i=0; i< length-1; i++)printf("%d,", a[i]);printf("%d", a[i]);printf("\n");
}#define N 9
int main()
{int i;int arr[N] = {9,1,5,8,3,7,4,6,2};printf("排序前:");printArray(arr, N);ShellSort(arr, N);printf("排序后:");printArray(arr, N);
}

php语言代码:

function shellSort(array $arr)
{$length = count($arr);$increment = $length;$k = 0;do{$increment = floor($increment/3) + 1;for($i = $increment; $i < $length; $i++){if($arr[$i] < $arr[$i - $increment]){$tmp = $arr[$i];$arr[$i] = $arr[$i - $increment]; //将 i-increment对应的值 后移一位for($j = $i - 2*$increment; $j >= 0 && $tmp  < $arr[$j]; $j -= $increment){$arr[$j + $increment] = $arr[$j];   //记录后移}$arr[$j + $increment] = $tmp;}}printf("increment=%d ,第%d次分组排序结果:\n", $increment, ++$k);print_r($arr);}while($increment > 1);return $arr;
}

描述:

按 步距 increment 来进行分组

依次轮流每一个组的一个元素 ,来进行插入排序 (注意不是:完全地把一个组里的元素 都 排完序,再进行下一组。)

4.2 希尔排序的 时间复杂度

4.3 希尔排序的 稳定性

不稳定

5. 堆排序

6. 归并排序

采用分治法(Divide and Conquer) 。 将已有序 的 子序列 合并, 得到完全有序的 序列。

6.1 代码实现

C语言代码:

#include <stdio.h>
#include <stdlib.h>// 声明打印函数
void printArray(int a[], int length);/*** 比较合并2个 子序列*/
void Merge(int sourceArr[], int tempArr[], int startIndex, int midIndex, int endIndex)
{int i = startIndex, j=midIndex + 1, k = startIndex;while(i != midIndex+1 && j!= endIndex + 1){if(sourceArr[i] > sourceArr[j])tempArr[k++] = sourceArr[j++];else tempArr[k++] = sourceArr[i++];}while(i != midIndex + 1)tempArr[k++] = sourceArr[i++];while(j != endIndex + 1)tempArr[k++] = sourceArr[j++];for(i= startIndex; i<= endIndex; i++)sourceArr[i] = tempArr[i];
}/*** 划分子序列,并递归划分子序列,并最后,调用“比较合并2个 子序列”*/
void MergeSort(int sourceArr[], int tempArr[], int startIndex, int endIndex)
{int midIndex;if(startIndex < endIndex){midIndex = startIndex + (endIndex - startIndex) / 2; //MergeSort(sourceArr, tempArr, startIndex, midIndex);MergeSort(sourceArr, tempArr, midIndex+1, endIndex);Merge(sourceArr, tempArr, startIndex, midIndex,endIndex);}
}/***  打印数组*/
void printArray(int a[], int length)
{int i;for(i=0; i< length-1; i++)printf("%d,", a[i]);printf("%d", a[i]);printf("\n");
}#define N 9
int main()
{int i;int arr[N] = {9,1,5,8,3,7,4,6,2};printf("排序前:");printArray(arr, N);//ShellSort(arr, N);int b[9];MergeSort(arr, b, 0, 8);printf("排序后:");printArray(arr, N);
}

描述:

第一步:申请空间,使其大小为 两个已排序序列之和,该空间用来存放合并后的序列。

第二步:设定两个指针,最初的位置分别为 两个已经排序序列的 起始位置。

第三步:比较两个指针指向的元素,选择相对较小的元素放到合并空间,并移动指针到下一个位置。

重复步骤3  直到某一个指针超出序列范围

将另一序列 所剩下的所有元素 直接复制 到合并 序列尾。

非递归算法:

6.2 归并排序 算法 复杂度

TimeSort  是 归并排序的 终极优化版本, 主要思想是:检测序列中的天然有序子段(若检测到严格降序子段则翻转序列为升序子段)。在最好情况下无论升序还是降序都可以使时间复杂度降至为O(n),具有很强的自适应性。

6.3 算法稳定性:

稳定

6.3 用途

6.4 比较

7.  快速排序

流程描述:

1: 首先设定一个分界值,通过该分界将数组分成左右两部分。

2: 将大于 分界值的数据 集中到 右边 ,小于等于 分界值的 集中到 左边。

3: 然后 左边和 右边 数据 又可以独立排序。对于左侧的数据,又可以取 一个分界值 ,同样的 右侧的数据,又可以取一个分界值。

4: 重复 上述的过程。当左右两个部分的数据 排序完成后,整个数组的排序也就完成了。

7.1 代码实现

C语言代码:

/*** 快速排序*/
void QuikSort(int *a, int low, int high)
{if(low >= high){return ;}int i= low;int j = high;int key = a[low];while(i < j){while(i < j && key <= a[j]){j--;  /* 向前寻找 */}a[i] = a[j];while(i < j && key >= a[i]){i++;}a[j] = a[i];}a[i] = key ; QuikSort(a, low, i -1);QuikSort(a, i+1, high);
}

7.2  快速排序 算法复杂度

排序(数据结构与算法)相关推荐

  1. 堆排序-排序-数据结构和算法

    1 前言 堆排序(英语:Heapsort)是指利用堆这种数据结构所设计的一种排序算法.堆是一个近似完全二叉树的结构,并同时满足堆积的性质:即子结点的键值或索引总是小于(或者大于)它的父节点. 关于数据 ...

  2. 快速排序-排序-数据结构和算法

    文章目录 1 基本算法 1.1 原地切分 1.2 边界 1.3 随机性 1.4 终止循环 1.5 切分元素重复 1.6 终止递归 2 备注 1 基本算法 ​ 快速排序是一种分治的排序算法.它将一个数组 ...

  3. 希尔排序 - 数据结构和算法91

    希尔排序 让编程改变世界 Change the world by program   这节课在开始讲解算法前小甲鱼先给大家一道智力题玩玩: ...... 省略,具体请看视频讲解 ...... 希尔排序 ...

  4. 【数据结构与算法】八大排序

    [数据结构与算法]八大排序 数据结构与算法-八大排序 排序的概念及其应用 排序的概念 排序的应用 常见的排序算法实现 常见的排序算法 插入排序 直接插入排序 希尔排序(缩小增量排序) 希尔排序的时间复 ...

  5. 获取用户列表为空_数据结构和算法(Golang实现)(15)常见数据结构-列表

    列表 一.列表 List 我们又经常听到 列表 List 数据结构,其实这只是更宏观的统称,表示存放数据的队列. 列表 List:存放数据,数据按顺序排列,可以依次入队和出队,有序号关系,可以取出某序 ...

  6. vrp 节约算法 c++_数据结构和算法(Golang实现)(8.1)基础知识-前言

    基础知识 学习数据结构和算法.我们要知道一些基础的知识. 一.什么是算法 算法(英文algorithm)这个词在中文里面博大精深,表示算账的方法,也可以表示运筹帷幄的计谋等.在计算机科技里,它表示什么 ...

  7. 02优先队列和索引优先队列-优先队列-数据结构和算法(Java)

    文章目录 1 概述 1.1 需求 1.2 优先队列特点 1.3 优先队列分类 1.4 应用场景 1.5 相关延伸 2 说明 3 索引优先队列 3.1 实现思路 3.2 API设计 3.2 代码实现及简 ...

  8. 使用Python和C++的写数据结构和算法

    使用Python和C++的写数据结构和算法 1.数据结构和算法简介 2.数据结构 2.1 堆栈 2.2 队列 2.3 散列表 2.4 二叉树 2.5 线性搜索 2.6 二进制搜索 2.7 递归 2.8 ...

  9. 数据结构与算法:选择排序

    数据结构与算法:选择排序 雪柯 大工生物信息 提笔为写给奋进之人 已关注 8 人赞同了该文章 引用自算法图解,作者[美] Aditya Bhargava 译袁国忠 特别备注:本书非原创,但部分内容自己 ...

  10. 数据结构与算法笔记 —— 十大经典排序及算法的稳定性

    一.十大经典排序算法 排序算法是<数据结构与算法>中最基本的算法之一. 排序算法可以分为内部排序和外部排序,内部排序是数据记录在内存中进行排序,而外部排序是因排序的数据很大,一次不能容纳全 ...

最新文章

  1. 第3次翻译了 Pandas 官方文档,叒写了这一份R万字肝货操作!
  2. python中的for else
  3. jquery json
  4. Nginx深入详解之模块化体系结构
  5. 年度总结、下年计划不会做?教给你一个一招致胜的方法
  6. oracle32位客户端安装教程,Win7系统32位Oracle11g客户端安装详述
  7. android.mk 编译jar包,Androidmk 使用详解
  8. 快速开发基于 HTML5 网络拓扑图应用--入门篇(一)
  9. 对复杂字典DictionaryT1,T2排序问题
  10. 阶段3 1.Mybatis_01.Mybatis课程介绍及环境搭建_07.环境搭建的注意事项
  11. 发布一个 host 管理插件
  12. 【Linux运维】01-Linux运维概述与CentOS系统安装
  13. 泛微 E9开发视频教程,零基础泛微开发
  14. 冲激脉冲抽样定理matlab,信号抽样及抽样定理分析.doc
  15. 根据IE窗口句柄hWnd获得IWebBrowser接口
  16. 树莓派GPIO 基础(一)
  17. 华为nova5里面有用到鸿蒙吗,华为nova7se是不是鸿蒙系统?
  18. Java的自动装箱与拆箱详细分析
  19. Microsoft Academic Search 微软学术搜索体验
  20. 网页布局——盒子模型

热门文章

  1. 安卓微信浏览器唤起系统内部浏览器
  2. surface go写php,【反馈】超便宜:851rmb的Surface go - 笔记本电脑(Notebook)版 - 北大未名BBS...
  3. css 平移到某个位置_Html基本的动画效果(平移,旋转)
  4. 魏文王问扁鹊的注释_魏文王问扁鹊 魏文王问扁鹊:“子昆弟三人其孰最善为医?” 扁...
  5. 邀请码 java_java 生成邀请码
  6. Free Video to JPG Converter(视频转图片的软件)简体中文版V5.0.101.201 | 如何将视频转图片
  7. 阿里面试官问我:如何设计秒杀系统?我给出接近满分的回答
  8. java如何创建一个文本框_创建一个有文本框和三个按钮的程序。当按下某个按钮时,使不同的文字(Java..._考试资料网...
  9. c51语言 延时程序值怎么算,C51的延时计算
  10. Navicat安装及简单使用