算法系列笔记1(排序)

本次主要记录一些经典的排序算法,其中包括冒泡排序、直接选择排序、插入排序、归并排序、快速排序、堆排序、希尔排序、桶排序以及计数排序和基数排序。首先会给出这些排序算法的基本思想,然后给出实现的代码,最后会给出其时间复杂度。

1:冒泡排序

思想:

(1):比较相邻的前后两个元素,如果后面的数据小于前面的数据,则交换这两个数据的位置。这样经过一次遍历,最小的元素将在第0个位置,属于”冒泡”.

(2):重复第一步,依次将第二小…的元素排列到数组的顶端。

// 交换数据的三种方法
void swap(int &t1, int &t2){
	int tmp = t1;
	t1 = t2;
	t2 = tmp;
}
<pre name="code" class="cpp">//  使用异或进行交换
void swap2(int &a, int &b){
	if(a != b){       ///  防止swap2(a, a)自身交换自身
		a ^= b;
		b ^= a;
		a ^= b;
	}
}

void swap3(int &a, int &b){
	if(a != b){
		a = a + b;
		b = a - b;
		a = a - b;
	}
}

#include <iostream>
using namespace std;

//  可以进行优化 设置监视哨,当该趟没有进行任何交换时,则退出  时间复杂度为n^2

void swap(int &t1, int &t2){
	int tmp = t1;
	t1 = t2;
	t2 = tmp;
}

void bubbleSort(int r1[], int n){
	for(int i = 0; i < n; i++)
	{
		for(int j = n-1; j > i; j--){
			if(r1[j] < r1[j-1]){
				swap(r1[j], r1[j-1]);
			}
		}
	}

}
int main(){
	int a[10] ={6,5,4,7,2,4,1,8,5,10};
	bubbleSort(a, 10);
	cout <<"冒泡排序后: ";
	for(int i = 0; i < 10; i++)
	{
		cout << a[i] << " ";
	}
	cout << endl;
	return 0;
}

时间复杂度:O(n^2)  为稳定的排序算法

注意:可以进行优化设置监视哨,当该趟没有进行任何交换时,则退出。

2:直接选择排序

思想:第一次从R[0]~R[n-1]中选取最小值,与R[0]交换,第二次从R[1]~R[n-1]中选取最小值,与R[1]交换,....,第i次从R[i-1]~R[n-1]中选取最小值,与R[i-1]交换,.....,第n-1次从R[n-2]~R[n-1]中选取最小值,与R[n-2]交换,总共通过n-1次,得到一个按排序码从小到大排列的有序序列.

代码:

#include <iostream>
using namespace std;

// 使用临时变量进行交换
void swap(int &a, int &b){
	int tmp = a;
	a = b;
	b = tmp;
}

void display(int a[], int n){
	for(int i = 0; i < n; i++){
		cout << a[i] << " ";
	}
	cout << endl;
}

void SelectSort(int a[], int n){
	for(int i = 0; i < n-1; i++)
	{
		int minIndex = i;
		int minElement = a[minIndex];
		for(int j = i+1; j < n; j++)
		{
			if(a[j] < minElement){
				minElement = a[j];
				minIndex = j;
			}
		}
		swap3(a[i], a[minIndex]);
	}
}

int main(){
	int a[10] ={6,5,4,7,2,4,1,8,5,10};
	SelectSort(a, 10);
	display(a, 10);
	return 0;
}

时间复杂度:O(n^2)。 为不稳定的排序算法

3:插入排序

思想:(1)初始时,a[0]自成1个有序区,无序区a[1,..n-1]。令i=1

(2)令temp=a[i],j从i-1到0,如果a[j]>temp 则a[j+1]=a[j],这样就将a[i]并入当前的有序区a[0,…i-1]中形成a[0,..i]的有序区间。

(3)i++并重复第二步直到i==n-1。排序完成。

insertSort.h

#ifndef INSERTSORT
#define INSERTSORT
#include "sortAlg.h"

class InsertSort:public SortAlg{
public:
	InsertSort(int *is, const int &len):SortAlg(is, len){}
	void sort(){
		for(int i = 1; i < length; i++){
			int j = i-1, tmp = sortElement[i];
			while(sortElement[j] > tmp && j >= 0){
				sortElement[j+1] = sortElement[j];
				j--;
			}
			sortElement[j+1] = tmp;
		}
	}
};
#endif

平均时间复杂度为O(n^2),如果该序列已经排序好了,此时效率最高为O(n)。是稳定的排序算法。

4:归并排序

思想:(1)如果只有一个元素则已经排序好,返回。(2)分别归并排序A[1,…n/2]和A[n/2+1,…n]。

(1)   对两个已经归并好的序列进行归并O(n).

代码:

#include <iostream>
using namespace std;

void mergeSort(int r1[], int r2[], int s, int m , int t){
	int i = s, j = m+1;
	int k = s;
	while(i <= m && j <= t){
		if(r1[i] <= r1[j])
			r2[k++] = r1[i++];
		else
			r2[k++] = r1[j++];
	}
	if(i > m){
		while(j <= t)
			r2[k++] = r1[j++];
	}
	if(j > t){
		while(i <= m)
			r2[k++] = r1[i++];
	}
	for(int n = s; n <= t; n++){          ///  注意 这个是必不可少
		r1[n] = r2[n];
	}

}

void merge(int r1[], int r2[], int s, int t){
	if(s < t){
		int m = (s+t)/2;
		merge(r1, r2, s, m);   //  归并排序A[1,,n/2]
		merge(r1, r2, m+1, t);   // 归并排序A[n/2+1,, n]
		mergeSort(r1, r2, s, m, t);    // 对已排序的两个子序列进行归并
	}
}

void display(int *a, int length){
	for(int i = 0; i < length; i++){
		cout << a[i] << " ";
	}
	cout << endl;
}

int main(){

	int r1[10] = {3,2,5,1,7,4,9,7,8,7}, r2[10];
	merge(r1, r2, 0, 9);
	display(r2, 10);
	return 0;

}

时间复杂度:O(nlgn). 为稳定的排序算法

5:快速排序

思想:(1)通常选择一个元素作为主元x(通常为第一个元素,随机化快排则是随机选择一个元素,然后与第一个元素进行交换)。将数组A中小于x的元素放在其左边,大于等于x的元素放在其右边。<这步划分通常有两种方法,书中是挖坑填数,从两边进行。而另外一种方法则是从一边开始,代码也给出来了>。

(2)对左右子序列分别进行快速排序即可。

代码:

#include <iostream>
#include <ctime>
using namespace std;

void swap(int &t1, int &t2){
	int tmp = t1;
	t1 = t2;
	t2 = tmp;
}

//  MIT视频中划分方法  只从一边进行
int Partition(int r1[], int p, int q){
	int i = p, j = p+1;
	int pivot = r1[p];
	for(j = p+1; j <= q; j++){
		if(r1[j] < pivot){
			i++;
			swap(r1[i], r1[j]);
		}
	}
	swap(r1[i], r1[p]);
	return i;
}

// 书中划分方法   从两边进行分划  挖坑填数 分而治之法   不稳定排序算法
int Partition2(int r1[], int p, int q){
	int i = p, j = q;
	int pivot = r1[p];
	while(i < j){
		while(j > i && r1[j] > pivot)
			j --;
		if(j > i)
			r1[i++] = r1[j];
		while(i < j && r1[i] < pivot)
			i++;
		if(i < j)
			r1[j--] = r1[i];
	}
	r1[i] = pivot;
	return i;
}

// 随机化的快速排序算法
int randomPartition(int r1[], int p, int q){
	srand((unsigned)time(0));
	int randInt = rand()%(q-p+1)+p;          // 随机选择一个元素作为主元
	swap(r1[p], r1[randInt]);

	int i = p, j = p+1;
	int pivot = r1[p];
	for(j = p+1; j <= q; j++){
		if(r1[j] < pivot){
			i++;
			swap(r1[i], r1[j]);
		}
	}
	swap(r1[i], r1[p]);

	return i;

}

void QuickSort(int r1[], int p, int q){
	if(p < q){
		int m = Partition(r1, p, q);
		//int m = randomPartition(r1, p, q);
		QuickSort(r1, p, m-1);
		QuickSort(r1, m+1, q);
	}

}
int main(){
	int a[10] ={6,5,4,7,2,4,1,8,5,10};
	cout <<"排序前: ";
	for(int i = 0; i < 10; i++){
		cout << a[i] << " ";
	}
	cout << endl;
	QuickSort(a, 0, 9);

	cout <<"快排后: ";
	for(int i = 0; i < 10; i++)
	{
		cout << a[i] << " ";
	}
	cout << endl;
	return 0;
}

平均时间复杂度为O(nlgn)。当序列已经有序,此时为最坏情况,时间复杂度为O(n^2)。为不稳定的排序算法。

6:堆排序

堆排序首先需要理解的就是堆的存储,删除,插入及堆化的过程(可以参考:http://blog.csdn.net/morewindows/article/details/6709644)。堆排序的思想就是将已经建好的堆(小堆举例)的根元素与最后一个元素互换,然后对除最后一个元素的堆进行堆化变为二叉堆,这样最后一个元素就是最小元素,重复这个操作,此时得到的数组即为降序排列的数组。

代码:

#include <iostream>
using namespace std;

void display(int a[], int n){
	for(int i = 0; i < n; i++){
		cout << a[i] << " ";
	}
	cout << endl;
}

void swap(int &a, int &b){
	int tmp = a;
	a = b;
	b = tmp;
}

//  用数组来存储堆, 插入元素相当于在数组最后插入元素  向上进行堆化
void minHeapFixUp(int a[], int i){
	int tmp = a[i];
	int j = (i-1)/2;
	while(j >= 0 && i!= 0){      //  没有i!=0 会出现死循环
		if(a[j] <= tmp) break;
		a[i] = a[j];
		i = j;
		j = (i-1)/2;        // -1/2 == 0
	}
	a[i] = tmp;
}

// 在最小堆中添加元素
void minHeapAddNumber(int a[], int n, int x){
	a[n-1] = x;
	minHeapFixUp(a, n-1);
}

// 删除元素都是删除第0个元素,并将最后一个元素放在第0个元素的位置 进行堆化
void minHeapFixDown(int a[], int i, int n){        // 向下 删除元素
	int j = 2*i +1;
	int tmp = a[i];
	while(j < n){
		if(j+1 < n && a[j+1] < a[j])
			j++;
		if(tmp <= a[j]) break;
		a[i] = a[j];
		i = j;
		j = j*2+1;
	}
	a[i] = tmp;
}

// 在最小堆中删除数
void minHeapDeleteNumber(int a[], int n){
	swap(a[0], a[n-1]);
	minHeapFixDown(a, 0, n-2);
}

//  堆化数组 建立最小堆
void makeMinHeap(int a[], int n){
	for(int i = n/2 - 1; i >= 0; i--){
		minHeapFixDown(a, i, n);
	}
}
// 堆排序
void minHeapSortToDes(int a[], int n){
	for(int i = n-1; i > 0; i--){
		swap(a[0], a[i]);
		minHeapFixDown(a, 0, i);
	}
}

int main(){
	int a[11] = {9, 12, 17,30, 50, 20, 60, 65, 4, 19};
	// 建堆
	makeMinHeap(a, 10);
	display(a, 10);

	// 添加元素
	minHeapAddNumber(a, 11, 3);
	display(a, 11);
	// 删除元素
	//minHeapDeleteNumber(a, 11);
	//display(a, 11);
	// 堆排序

	minHeapSortToDes(a, 11);
	display(a, 11);
	return 0;
}

时间复杂度为O(nlgn)。为不稳定的排序算法。

7:希尔排序

思想:先将整个待排元素序列分割成若干个子序列(由相隔某个“增量”的元素组成的)分别进行直接插入排序,然后依次缩减增量再进行排序,待整个序列中的元素基本有序(增量足够小)时,再对全体元素进行一次直接插入排序。因为直接插入排序在元素基本有序的情况下(接近最好情况),效率是很高的,因此希尔排序在时间效率上比前两种方法有较大提高。

步长一般以n/2开始,每次减半,直到最后为1.

代码:

#include <iostream>
using namespace std;

void ShellSort(int a[], int n){
	for(int gap = n/2; gap > 0; gap --){   // 步长
		for(int i = gap; i < n; i++){
			int temp = a[i];
			int j = i-gap;
			while(j >= 0 && a[j] > temp){   // 进行直接插入排序
				a[j+gap] = a[j];
				j -= gap;
			}
			a[j+gap] = temp;
		}
	}
}

void display(int a[], int n){
	for(int i = 0; i < n; i++){
		cout << a[i] << " ";
	}
	cout << endl;
}
int main(){
	int a[10] ={6,5,4,7,2,4,1,8,5,10};
	ShellSort(a, 10);
	display(a, 10);
	return 0;
}

时间复杂度一般要取决于步长,在O(nlgn)与O(n^2)之间,为不稳定排序。

8:桶排序

桶排序 (Bucket sort)或所谓的箱排序,是一个排序算法,工作的原理是将阵列分到有限数量的桶子里。每个桶子再个别排序(有可能再使用别的排序算法或是以递回方式继续使用桶排序进行排序)。桶排序是鸽巢排序的一种归纳结果。当要被排序的阵列内的数值是均匀分配的时候,桶排序使用线性时间(Θ(n))。但桶排序并不是 比较排序,他不受到 O(n log n) 下限的影响。

例如要对大小为[1..1000]范围内的n个整数A[1..n]排序,可以把桶设为大小为10的范围,具体而言,设集合B[1]存储[1..10]的整数,集合B[2]存储(10..20]的整数,……集合B[i]存储((i-1)*10,i*10]的整数,i = 1,2,..100。总共有100个桶。然后对A[1..n]从头到尾扫描一遍,把每个A[i]放入对应的桶B[j]中。 然后再对这100个桶中每个桶里的数字排序,这时可用冒泡,选择,乃至快排,一般来说任何排序法都可以。最后依次输出每个桶里面的数字,且每个桶中的数字从小到大输出,这样就得到所有数字排好序的一个序列了。

假设有n个数字,有m个桶,如果数字是平均分布的,则每个桶里面平均有n/m个数字。如果对每个桶中的数字采用快速排序,那么整个算法的复杂度是O(n+m*n/m*log(n/m))=O(n+nlogn-nlogm)

从上式看出,当m接近n的时候,桶排序复杂度接近O(n)

当然,以上复杂度的计算是基于输入的n个数字是平均分布这个假设的。这个假设是很强的,实际应用中效果并没有这么好。如果所有的数字都落在同一个桶中,那就退化成一般的排序了。

#include <iostream>
using namespace std;

//  MIT视频中划分方法  只从一边进行
int Partition(int r1[], int p, int q){
	int i = p, j = p+1;
	int pivot = r1[p];
	for(j = p+1; j <= q; j++){
		if(r1[j] < pivot){
			i++;
			swap(r1[i], r1[j]);
		}
	}
	swap(r1[i], r1[p]);
	return i;
}

void QuickSort(int r1[], int p, int q){
	if(p < q){
		int m = Partition(r1, p, q);
		//int m = randomPartition(r1, p, q);
		QuickSort(r1, p, m-1);
		QuickSort(r1, m+1, q);
	}

}

// 每个桶中的元素 为一个数组 并且记录了该桶中存放元素的数目
struct bucket{
	int node[10];      // 应该为待排序数组的大小  这里简单的定义为了10 很局限
	int count;
};

void BucketSort(int r1[], int n){
	//  桶的大小为10
	bucket* pBucket = new bucket[10];
	// 初始化桶
	for(int i = 0; i < 10; i++){
		(pBucket+i)->count = 0;
	}

	int maxNum = r1[0], minNum = r1[0];

	for(int i = 1; i < n; i++){
		if(r1[i] > maxNum) maxNum = r1[i];
		if(r1[i] < minNum) minNum = r1[i];
	}

	// 将数据放入桶中
	for(int i = 0; i < n; i++)
	{
		int index = ((r1[i]-minNum)/(maxNum-minNum))*9;
		(pBucket+index)->node[(pBucket+index)->count] = r1[i];
		(pBucket+index)->count++;
	}

	// 对每个桶进行快排  或者其它排序方法
	int k = 0;
	for(int i = 0; i < 10; i++){
		QuickSort((pBucket+i)->node, 0, (pBucket+i)->count-1);
		for(int j = 0; j < (pBucket+i)->count; j++){
			r1[k++] = (pBucket+i)->node[j];
		}
	}
	delete []pBucket;
}

void display(int a[], int n){
	for(int i = 0; i < n; i++){
		cout << a[i] << " ";
	}
	cout << endl;
}

int main(){
	int a[10] ={6,5,4,7,2,4,1,8,5,10};
	BucketSort(a, 10);
	display(a, 10);
	return 0;
}

时间复杂度O(n),为不稳定排序算法

9:计数排序和基数排数

这两个排序算法都是线性时间排序算法,计数排序需假设输入数据为一个小区间内的整数,为小规模的数字。而基数排序一般用来在线性时间内处理大范围的数据。

计数排序的代码:

#include <iostream>
using namespace std;

void CountSort(int r1[], int c[], int n, int k, int r2[]){
	for(int i = 0; i < k; i++) c[i] = 0;

	for(int i = 0; i < n; i++) c[r1[i]]++;   // 统计每个元素出现的次数

	for(int i = 1; i < k; i++) c[i] = c[i-1] + c[i];        // 统计小于等于该元素的个数  即该元素即将放在r2中的位置

	//for(int i = n-1; i >= 0; i--){     // 为了保证该算法是稳定的
	for(int i = 0; i < n; i++){
		r2[c[r1[i]] - 1] = r1[i];
		c[r1[i]] = c[r1[i]] - 1;
	}
}

int main(){
	int a[10] = {2,3,1,4,5,4,2,3,0,3}, b[10];
	int c[6];

	cout << "排序前: ";
	for(int i = 0; i < 10; i++){
		cout << a[i] <<" ";
	}
	cout << endl;
	CountSort(a, c, 10, 6, b);

	cout << "计数排序后: ";
	for(int i = 0; i < 10; i++){
		cout << b[i] <<" ";
	}
	cout << endl;

	return 0;
}

基数排序代码可能局限于实际的应用,先从个位排序,再到十位,最后到高位等,可以对每位采用桶排序。

它们的时间复杂度都为O(n)

总结:(1)冒泡排序、直接选择排序、插入排序、归并排序、快速排序、堆排序、希尔排序为基于比较的排序算法,其平均时间复杂度不会低于O(nlgn)。而计数和基数排序都为线性时间排序算法。桶排序比较特殊

(2) 冒泡排序、直接选择排序、插入排序的平均时间复杂度为O(n^2)。归并排序、快速排序、堆排序的平均时间复杂度为O(nlgn),为渐进最好的比较排序算法。桶排序、计数排序及基数排序为O(n)

(3)直接选择排序、快速排序、堆排序、希尔排序(跨跃步长交换)和桶排序都为不稳定的排序算法,他们都跨跃交换了。其它的都为稳定排序算法。

参考文献

1:http://blog.csdn.net/column/details/algorithm-easyword.html白话白话经典算法系列

2:http://blog.csdn.net/houapple/article/details/6480100桶排序

时间: 2024-11-05 02:39:16

算法系列笔记1(排序)的相关文章

算法系列笔记5(扩展数据结构-动态顺序统计和区间树)

在编程中,我们往往使用已有的数据结构无法解决问题,这是不必要急着创建新的数据结构,而是在已有数据结构的基础上添加新的字段.本节在上一次笔记红黑树这一基础数据结构上进行扩展,得出两个重要的应用-动态顺序统计和区间树. 动态顺序统计 在算法系列笔记2中我们在线性时间内完成了静态表的顺序统计,而这里我们在红黑树上进行扩展,在O(lgn)时间内完成该操作,主要包括返回第i 排名的元素os_select(i)和给定一个元素x,返回其排名(os_rank(x)). 思想:添加新项:在红黑树的结点上记录下该结

(转) 白话经典算法系列之三 希尔排序的实现(附源代码实现)

链接:http://blog.csdn.net/morewindows/article/details/6668714 希尔排序的实质就是分组插入排序,该方法又称缩小增量排序,因DL.Shell于1959年提出而得名. 该方法的基本思想是:先将整个待排元素序列分割成若干个子序列(由相隔某个“增量”的元素组成的)分别进行直接插入排序,然后依次缩减增量再进行排序,待整个序列中的元素基本有序(增量足够小)时,再对全体元素进行一次直接插入排序.因为直接插入排序在元素基本有序的情况下(接近最好情况),效率

白话经典算法系列之三 希尔排序的实现

分类: 白话经典算法系列 2011-08-08 11:41 47406人阅读 评论(46) 收藏 举报 算法shell优化c 希尔排序的实质就是分组插入排序,该方法又称缩小增量排序,因DL.Shell于1959年提出而得名. 该方法的基本思想是:先将整个待排元素序列分割成若干个子序列(由相隔某个“增量”的 元素组成的)分别进行直接插入排序,然后依次缩减增量再进行排序,待整个序列中的元素基本有序(增量足够小)时,再对全体元素进行一次直接插入排序.因为 直接插入排序在元素基本有序的情况下(接近最好情

排序算法系列:奇偶排序算法

概述 在上一篇中我们说到了冒泡排序的原理及实现详解.冒泡排序是一种交换排序,本文还是接着上一讲,说说另一种交换排序算法--奇偶排序. 版权说明 本文链接:http://blog.csdn.net/lemon_tree12138/article/details/50605563 – Coding-Naga - 转载请注明出处 目录 概述 版权说明 目录 奇偶排序算法 算法原理 算法原理图 算法步骤 算法可行性证明 算法过程图 算法实现 算法复杂度分析 Ref GitHub源码下载 奇偶排序算法 奇

数据结构与算法系列研究九——排序算法的一些探讨

四种排序 一.实验内容     输入20个整数,分别用希尔排序.快速排序.堆排序和归并排序实现由小到大排序并输出排序结果.二.关键数据结构与核心算法   关键数据结构:由于是排序为了简单起见,选用线性表中的数组作为存储结构.   核心算法:   1.希尔排序    希尔排序的核心还是直接插入法,但是插入的位置有所讲究.要把数组分为许多段,每一段的长度除了最后的有可能不同之外,其他的都相同.该段的长度即为增量,在最后一次必须为一,此时程序变成了直接插入.每次进行隔段插入,不断地调整是的数组变得隔段

数据结构与算法系列十(排序算法概述)

1.引子 1.1.为什么要学习数据结构与算法? 有人说,数据结构与算法,计算机网络,与操作系统都一样,脱离日常开发,除了面试这辈子可能都用不到呀! 有人说,我是做业务开发的,只要熟练API,熟练框架,熟练各种中间件,写的代码不也能“飞”起来吗? 于是问题来了:为什么还要学习数据结构与算法呢? #理由一: 面试的时候,千万不要被数据结构与算法拖了后腿 #理由二: 你真的愿意做一辈子CRUD Boy吗 #理由三: 不想写出开源框架,中间件的工程师,不是好厨子 1.2.如何系统化学习数据结构与算法?

算法系列笔记6(有关图的算法一—搜索,拓扑排序和强连通分支)

简单概念:对于图G(V,E),通常有两种存储的数据结构,一种是邻接矩阵,此时所需要的存储空间为O(V^2):第二种是邻接表,所需要的存储空间为O(V+E).邻接表表示法存在很强的适应性,但是也有潜在的不足,当要快速的确定图中边(u,v)是否存在,只能在顶点u的邻接表中搜索v,没有更快的方法,此时就可以使用邻接矩阵,但要以占用更多的存储空间作为代价:此外当图不是加权的,采用邻接矩阵存储还有一个优势:在存储邻接矩阵的每个元素时,可以只用一个二进位,而不必用一个字的空间. 图的搜索算法 搜索一个图示有

算法系列【希尔排序】篇

常见的内部排序算法有:插入排序.希尔排序.选择排序.冒泡排序.归并排序.快速排序.堆排序.基数排序等.用一张图概括: 关于时间复杂度: 1.     平方阶 (O(n2)) 排序各类简单排序:直接插入.直接选择和冒泡排序. 2.     线性对数阶 (O(nlog2n)) 排序快速排序.堆排序和归并排序: 3.     O(n1+§))排序,§ 是介于 0 和 1 之间的常数.希尔排序 4.     线性阶 (O(n)) 排序基数排序,此外还有桶.箱排序. 关于稳定性: 稳定的排序算法:冒泡排序

排序算法系列——直接选择排序

前面两篇介绍了两种插入排序算法:直接插入排序和希尔排序.这篇介绍选择排序的一种:直接选择排序.从名字就可以看出直接选择排序与直接插入排序很相似,两者相同点在与都是将待排序序列分成有序区和无序区两部分,不同之处在于直接插入排序是从无序区选出一个插入到有序区合适的位置,而直接选择排序是从无序区选出最小的一个插入到有序区尾部,使得有序区保持有序. 基本思想: 一组待排序的数据,首先将其划分成两部分,一部分是已排好序的,另一部分是待排序的,然后依次从待排序部分取出最小的元素插入到已排序部分的尾部,保证有