数据结构与算法(一)——排序

虽然之前学过数据结构,但是已时隔四年,大概四月份复习了一遍,但是很多概念也是一知半解,所以重新整理知识点和运行代码的方式来巩固知识。

引言

  • 排序:是计算机程序设计中的一种重要操作,功能是将一个数据元素的任意序列重新排列成一个按关键字的有序序列

  • 根据待排序记录的存储器的不同可分为

    sort_methods

  • 各个排序算法的性能比较

    sort_algorithm_compare

冒泡排序

算法说明

冒泡排序是一种交换排序。那什么是交换排序呢?

交换排序:两两比较待排序的关键字,并交换不满足次序要求的那对数,直到整个表都满足次序要求为止。

  • 基本思想:通过相邻记录两两比较交换的方式,找到每次迭代中数据的最大值,最终达到排序的目的。

  • 算法描述

    • 首先将第一个记录的关键字与第二个记录的关键字进行比较,若为逆序(L.r[1].key>L.r[2].key),则将两个记录交换,然后比较第二个记录和第三个记录的关键字,依次类推,直到第n-1个记录和第n个记录进行比较为止。这是一趟排序。
    • 然后进行第二趟排序,对前n-1个记录进行同样的操作。重复这样的操作。
    • 直到冒泡排序在一趟拍讯中没有进行过交换记录的操作为止。

这个算法的名字由来是因为越小的元素会经由交换慢慢“浮”到数列的顶端,故名。

用例说明

冒泡排序

代码实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<?php
$list = array(49, 38, 65, 97, 76, 13, 27, 49);
$res = BubbleSort($list);
print_r($res);

function BubbleSort($list) {
$len = count($list);
for ($i = 0; $i < $len; $i++) {
for ($j = $len-1; $j > $i; $j--) {
if ($list[$j-1] > $list[$j]) {
swap($list, $j-1, $j);
$bChange = true;
}
}
}
echo $k;
return $list;
}

function swap(&$list, $i, $j) {
$temp = $list[$i];
$list[$i] = $list[$j];
$list[$j] = $temp;
}
?>

输出:

1
2
3
4
5
6
7
8
9
10
11
Array
(
[0] => 13
[1] => 27
[2] => 38
[3] => 49
[4] => 49
[5] => 65
[6] => 76
[7] => 97
)

快速排序

算法说明

快速排序是冒泡排序的改进,整个快速排序的过程是一个递归的过程

基本思想:通过一趟排序将待排记录分割成独立两部分,其中一部分记录的关键字均比另一部分记录的关键字小,则可分别对着两部分记录继续记性排序,以达到整个序列有序。

算法描述

  • 一趟快速排序的步骤:
    • 设两个指针low和high,设枢轴记录的关键字为pivotkey;
    • 先从high所指向的位置向前搜索找到第一个小于pivotkey的记录和枢轴记录互相交换;
    • 然后从low所指向的位置向后搜索找到第一个大于pivotkey的记录和枢轴记录互相交换;
    • 重复前面两步骤,知道low=high为止。
  • 取当前的pivotkey记录所在的位置(枢轴位置),将整个记录分割为两个子序列,分别进行快速排序;
  • 直到待排序列中只有一个记录为止。

用例说明

简单快速排序

代码实现

先将枢轴记录暂存在r[0]的位置上,排序过程只作r[row]或者r[high]的单向移动,知道一趟排序结束后,再将枢轴记录移至正确的位置上。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#include<iostream>  
using namespace std;
// 声明快排函数
void quickSort(int a[],int,int);

int main()
{
int array[]={34,65,12,43,67,5,78,10,3,70},k;
int len=sizeof(array)/sizeof(int);
cout<<"The orginal arrayare:"<<endl;
for(k=0;k<len;k++)
cout<<array[k]<<",";
cout<<endl;
quickSort(array,0,len-1);
cout<<"The sorted arrayare:"<<endl;
for(k=0;k<len;k++)
cout<<array[k]<<",";
cout<<endl;
system("pause");
return 0;
}

void quickSort(int s[], int l, int r) {
if (l< r) {
int i = l, j = r, x = s[l];
while (i < j) {
while(i < j && s[j]>= x) // 从右向左找第一个小于x的数
j--;
if(i < j)
s[i++] = s[j];
while(i < j && s[i]< x) // 从左向右找第一个大于等于x的数
i++;
if(i < j)
s[j--] = s[i];
}
s[i] = x;
quickSort(s, l, i - 1); // 递归调用
quickSort(s, i + 1, r);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
<?php
$list = array(49, 38, 65, 97, 76, 13, 27, 49);//这个用例输出的是null
//$list = array(9, 1, 5, 8, 3, 7, 4, 6, 2);//这个用例测试结果是对的
$res = QuickSort($list);
print_r($res);
print_r($list);
function QuickSort($list) {
$len = count($list);
Qsort($list, 0, $len-1);
return $list;
}
function Qsort(&$list, $low, $high) {
if ($low < $high) {
$pivot = Partition($list, $low, $high);
Qsort($list, $low, $pivot-1);
Qsort($list, $pivot+1, $high);
}
}
function Partition(&$list, $low, $high) {
$pvalue = $list[$low];
while ($low < $high) {
while ($low < $high && $list[$high] > $pvalue) {
$high--;
}
swap($list, $low, $high);
while ($low < $high && $list[$low] < $pvalue) {
$low++;
}
swap($list, $low, $high);
}
return $low;
}
function swap(&$list, $i, $j) {
$temp = $list[$i];
$list[$i] = $list[$j];
$list[$j] = $temp;
}
?>

直接插入排序

算法说明

直接插入排序是一种最简单的排序方法,它的基本操作是将一个记录仪插入到已排好序的有序表中,从而得到一个新的、记录数增1的有序表。

算法描述

  • 先取代排序序列中的第一个记录作为一个有序序列;
  • 取第二个记录,在这个有序序列依次查找比它小的最大关键字,将该记录插入到查找的关键字位置后面;
  • 取第i个记录按同种方法插入到当前有序序列;
  • 将最后一个记录插入,即可完成排序。

用例说明

直接插入排序

代码实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?php
$list = array(49, 38, 65, 97, 76, 13, 27, 49);
$res = InsertionSort($list);
print_r($res);

function InsertionSort($list){
$len = count($list);
for ($i = 1; $i < $len; $i++) {
if ($list[$i] < $list[$i-1]) {
$target = $list[$i];
for($j = $i-1; $j >= 0 && $list[$j] >= $target; $j--) {
$list[$j+1] = $list[$j];
}
$list[$j+1] = $target;
}
}
return $list;
}
?>

希尔排序

算法说明

希尔排序又称“缩小增量排序”,也属于插入排序类方法,但时间效率更高。希尔排序的一个特点:子序列的构成不是简单地“逐段分割”,而是将相隔某个“增量”的记录组成一个子序列。

算法描述

  • 将整个待排序序列分割成若干个子序列
  • 对每个子序列分别进行直接插入排序
  • 待每个子序列中的记录有序时,再缩小增量重新分割子序列,重复上一步操作,直到增量为1,进行最后一次排序后即完成排序。

这个算法,主要是通过增量划分子序列,使序列基本有序,减小插入排序的复杂度。

需注意的是,到目前为止,尚未有人求得一种最好的增量序列,但是也有一些局部结论。增量序列可以用多种取法,但需注意:应使增量序列中的值没有除1以外的公因数,并且最后一个增量值必须等于1

用例说明

希尔排序

代码实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?php
$list = array(49, 38, 65, 97, 76, 13, 27, 49);
$res = ShellSort($list);
print_r($res);
function ShellSort($list) {
$len = count($list);
$increment = $len;
do {
$increment = floor($increment/3) + 1;
for ($i = $increment; $i < $len; $i++) {
if ($list[$i] < $list[$i-$increment]) {
$target = $list[$i];
for ($j = $i-$increment; $j >= 0 && $list[$j] > $target; $j -= $increment) {
$list[$j+$increment] = $list[$j];
}
$list[$j+$increment] = $target;
}
}
}while($increment > 1);
return $list;
}
?>

简单选择排序

算法说明

简单选择排序是一种选择排序。每一趟在n-i+1(i=1,2,…,n-1)个记录中选择关键字最小的记录作为有序序列的第i个记录。

算法描述

  • 先从n个记录(待排序列)中找到最小的关键字,作为第一个记录
  • 随后在剩下的n-1(n-i+1)子序列中再寻找最小关键字,作为第2(i)个记录
  • 依次类推,直到剩下最后的一个记录(即最大关键字)为止。

用例说明

简单选择排序

代码实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<?php
$list = array(49, 38, 65, 97, 76, 13, 27, 49);
$res = SelectionSort($list);
print_r($res);
function SelectionSort($list) {
$len = count($list);
for ($i = 0; $i < $len; $i++) {
$min = $i;
for ($j = $i; $j < $len; $j++) {
if ($list[$min] > $list[$j]) {
$min = $j;
}
}
if ($min != $i) {
swap($list, $min, $i);
}
}
return $list;
}
function swap(&$list, $i, $j) {
$temp = $list[$i];
$list[$i] = $list[$j];
$list[$j] = $temp;
}
?>

树形选择排序

算法说明

树形选择排序,又称“锦标赛排序”,是一种按照锦标赛的思想进行选择排序的方法,是对简单选择排序的一种改进。

算法描述

  • 首先对n个记录的关键字进行两两比较,选出较小者
  • 然后在其[n/2]个较小者之间再进行两两比较,再选出较小者
  • 如此重复,知道选出最小关键字的记录为止
  • 将最小关键字输出,然后根据关系的可传递性,选出次小关键字,依次输出,直到输出所有的记录。

用例说明

树形选择排序

堆排序

  • :一棵顺序存储的完全二叉树。

  • 小顶堆:其中每个结点的关键字都不大于其孩子结点的关键字,这样的堆称为小顶堆(小根堆)。

  • 大顶堆:其中每个结点的关键字都不小于其孩子结点的关键字,这样的堆称为大顶堆(大根堆)。

  • 举例来说

    对于n个元素的序列{R0, R1, … , Rn}当且仅当满足下列关系之一时,称之为堆:

    (1) Ri <= R2i+1 且 Ri <= R2i+2 (小顶堆)

    (2) Ri >= R2i+1 且 Ri >= R2i+2 (大顶堆)

    其中i=1,2,…,n/2向下取整

算法说明

  • 根据初始数组去构造初始堆(构建一个完全二叉树,保证所有的父结点都比它的孩子结点数值大)。
  • 每次交换第一个和最后一个元素,输出最后一个元素(最大值),然后把剩下元素重新调整为大顶堆。
  • 当输出完最后一个元素后,这个数组已经是按照从小到大的顺序排列了。

用例说明

堆排序

代码实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
<?php
$list = array(49, 38, 65, 97, 76, 13, 27, 49);
$res = HeapSort($list);
print_r($res);
function HeapSort($list) {
$len = count($list);
for ($i = floor(($len-2)/2); $i >= 0; $i--) {
createHeap($list, $i, $len-1);
}
for ($i = $len-1; $i >=0 ; $i--) {
swap($list, $i, 0);
createHeap($list, 0, $i-1);
}
return $list;
}
function createHeap(&$list, $i, $j) {
$target = $list[$i];
for ($m=$i*2+1; $m <= $j ; $m = $m*2+1) {
if ($m < $j && $list[$m] < $list[$m+1]) {
$m++;
}
if ($target > $list[$m]) {
break;
}
$list[$i] = $list[$m];
$i = $m;
}
$list[$i] = $target;
}
function swap(&$list, $i, $j) {
$temp = $list[$i];
$list[$i] = $list[$j];
$list[$j] = $temp;
}

归并排序

算法说明

归并排序是将两个或者两个以上的有序表组合成一个新的有序表。

归并排序是建立在归并操作上的一种有效的排序算法,该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。

将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并

算法描述

  • 将待排序序列R[0…n-1]看成是n个长度为1的有序序列,将相邻的有序表成对归并,得到n/2个长度为2的有序表;
  • 将这些有序序列再次归并,得到n/4个长度为4的有序序列
  • 如此反复进行下去,最后得到一个长度为n的有序序列。

归并排序其实要做两件事

(1)“分解”——将序列每次折半划分。

(2)“合并”——将划分后的序列段两两合并后排序。

用例说明

归并排序

代码实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
<?php
$list = array(49, 38, 65, 97, 76, 13, 27, 49);
$res = MergeSort($list);
print_r($res);
function MergeSort(&$list) {
$len = count($list);
$res = array();
Msort($list, $res, 0, $len-1);
return $res;
}
function MSort(&$list, &$res, $s, $t) {
if ($s == $t) {
$res[$s] = $list[$s];
}else{
$m = floor(($s + $t)/2);
$temp = array(); //新建一个空数组
MSort($list, $temp, $s, $m);
MSort($list, $temp, $m+1, $t);
Merge($temp, $res, $s, $m, $t);
}
}
function Merge(&$temp, &$res, $s, $m, $t) {
for ($k = $s, $j = $m+1; $s <= $m && $j <= $t; $k++) {
if ($temp[$s] < $temp[$j]) {
$res[$k] = $temp[$s];
$s++;
}else{
$res[$k] = $temp[$j];
$j++;
}
}
if ($s <= $m) {
for ($l = 0; $l <= $m-$s ; $l++) {
$res[$k+$l] = $temp[$s+$l];
}
}
if ($j <= $t) {
for ($l = 0; $l <= $t-$j ; $l++) {
$res[$k+$l] = $temp[$j+$l];
}
}
}
?>

基数排序

算法说明

代码实现

参考:

  1. 严蔚敏 等著《数据结构 C语言版》
  2. 程序员的内功——数据结构和算法系列
  3. 排序一 冒泡排序
  4. 排序二 快速排序
  5. 排序三 直接插入排序
  6. 排序四 希尔排序
  7. 排序五 简单选择排序
  8. 排序六 堆排序
  9. 排序七 归并排序
  10. 排序八 基数排序
  11. 用PHP实现的常用排序算法