数据结构与算法分析-排序

作者:xiabodan 出处:http://blog.csdn.net/xiabodan

排序算法(Sorting
Algorithm)是计算机算法的一个组成部分。也是程序=算法+数据结构中的一部分(算法)。

实验平台:raspberry 2 B +
Ubuntu Mate

插入排序

外循环i由1到N-1,内循环由j由i到1,每次内循环都将A【j】插入到序列A【0】-A【i】的正确位置,这样就保证了每次外循环之后序列A【0】-A【i】都是已经排好序的,外循环不过将自己插入在A【0】-A【i】之间,且不影响原来的序列正确性。和冒泡算法一样都要经过O(N^2)次比較和交换,例如以下图

//插入排序
//stable
//O(N^2) comparisons and swaps
//Adaptive: O(n) time when nearly sorted
//Very low overhead
void insertion(elementtype A[],int n)
{
	int p = 0 ;
	int j = 0 ;
	for(p=1;p<n;p++ )
	{
		elementtype tem = A[p] ;
		for(j=p;j>0&&A[j-1]>tem;j--)
		{
			 A[j] = A[j-1];
		}
		A[j] = tem;
	}

}

希尔排序

希尔排序有时能够成为增量缩小排序,选择一个大小为hk的区间,对相隔hk个元素进行插入排序,逐渐减小hk区间。直至hk变为1

//希尔排序
//O(N^3/2)   unstable
//Adaptive: O(N.lg(N)) time when nearly sorted
//Very low overhead
void shell(elementtype A[],int n)
{
	int i,j,inc;
	elementtype tem;

	for(inc=N/2;inc>0;inc /=2)
	{
		for(i=inc;i<N;i++)
		{
			tem = A[i];
			for(j=i;j>=inc;j-=inc)
			{
				if(tem<A[j-inc])
					A[j] = A[j-inc];
				else
					break;
			}
			A[j] = tem;
		}
	}
}

冒泡排序

冒泡排序比較暴力,外循环i从A【0】-A【N-1】,内循环从A【N-1】-A【i】,同一时候内循环从末尾開始一直向i靠近。并找出当中最小的元素冒出来到A【i+1】,故称为冒泡排序。十分暴力,但非常稳定,须要1+2+3+4+.......+N = N*(N+1) = O(N^2)次比較和交换,效率非常低。可是当某次内循环检測到没有发生交换后,说明A【N-1】-A【i】都是依照顺序排列的。不须要排序了。因此假设当给定数据是已经接近排序好的时候,冒泡算法时间复杂度仅为O(N)。

//冒泡排序
//O(N^2)   stable
//Adaptive: O(N) time when nearly sorted
//Very low overhead
void bubble(elementtype A[],int n)
{
	int flag = 1;
	int i,j;
	for(i=0;i<n;i++)
	{
		flag = 0;
		for(j=n-1;j>i;j--)
		{
			if(A[j]<A[j-1])
			{
				flag = 1;
				swap(A+j,A+j-1 );
			}

		}
		if(flag == 0) break;
	}
}

选择排序

选择排序,外循环i从A【0】-A【N-1】。内循环从A【i+1】-A【N-1】。内循环负责找出A【i+1】-A【N-1】中比A【i】小的元素,并用一个k去标记它的位置,在内循环结束的时候将A【i】与A【k】互换。那么A【i+1】-A【N-1】中比A【i】小的元素A【k】就被放在了A【i】这个位置。也就是选择一个最小的放在A【i】这个位置。

选择排序的比較次数是O(N^2)。可是交换次数却唯独O(N)。由于内循环每次不过标记最小元素,并没有实时的去交换元素。

//选择排序
//Not stable
//O(1) extra space
//Θ(n2) comparisons
//Θ(n) swaps
//Not adaptive
void selection(elementtype A[],int n)
{
	int i,j;
	int k;
	for(i=0;i<n;i++)
	{
		k = i;
		for(j=i+1;j<n;j++)
		{
			if(A[j]<A[k])
			{
				k = j;
			}
		}

		swap(A+i,A+k);
	}
}

高速排序

高速排序是一个典型的分治策略,就是将非常大的集合A划分为非常小的模块。逐一处理。首先须要选择一个枢纽元素pivot,然后将整个集合A分为两半,一办是都比pivot小的元素,集合Al放在左边。一组是比pivot大的元素都放在右边。叫做集合Ar。然后再对左右边的子集Al与Ar分别採取相同的方法递归。直至集合Al和Ar都唯独一个元素,在实际应用中,一般不会让Al和Ar都递归到1个元素。一种要的方法是当Al和Ar小于10(阈值CUT)后,就不在採用快排算法。而是换为插入排序算法效率更高。因为本文測试中不利于大数据的排序。所以只将阈值CUT设置为10。见下图

另外一个问题是枢纽元素pivot的选择,选择枢纽元素pivot的方法有非常多。一种经常使用的就是三数中值算法。也就是选取集合A首尾以及中位元素,然后选取这三个数的中位数作为枢纽元素pivot。

//高速排序
//not Stable
//O(lg(n)) extra space (see discussion)
//O(n2) time, but typically O(n·lg(n)) time
//Not adaptive
#define CUT 3
elementtype median3(elementtype A[],int left ,int right)
{
	int center = (left +right) / 2;
	if(A[left]>A[center])
		swap(&A[left],&A[center]);
	if(A[left]>A[right])
		swap(&A[left],&A[right]);
	if(A[center]>A[right])
		swap(&A[center],&A[right]);

	swap(&A[center],&A[right-1]);

	return A[right-1];
}
void Qsort(elementtype A[],int left, int right)
{
	int i,j;
	elementtype pivot;

	if(left + CUT<= right)
	{
		pivot = median3(A,left,right); //select middle element as pivot
		i = left;j = right-1;
		for(;;)
		{
			while(A[++i]<pivot){}

			while(A[--j]>pivot){}
			if(i<j)
				swap(&A[i],&A[j]);
			else
				break;
		}
		swap(&A[i],&A[right-1]);

		Qsort(A,left,i-1);
		Qsort(A,i+1,right);
	}
	else
		insertion(A+left,right-left+1);
}
void quick1(elementtype A[],int n)
{
	Qsort(A,0,n-1);
}

归并排序

归并排序的思想也是(divide-and-conquer)分治策略。将集合A递归均分。直至没组仅仅剩下一个元素时,在開始组合。组合原理例如以下图非常形象,就不多解释。很多其它关于归并算法能够參见博客http://geeksquiz.com/merge-sort/

//归并排序
//Stable
//(n) extra space for arrays (as shown)
//(lg(n)) extra space for linked lists
//(n·lg(n)) time
//Not adaptive
//Does not require random access to data
void Merge(elementtype A[],elementtype TA[],int lpos,int rpos,int rightend)
{
	int leftend = rpos-1;
	int numelement = rightend -lpos + 1;
	int tpos = lpos;

	while(lpos<=leftend && rpos<=rightend)
		if(A[lpos] <= A[rpos])
			TA[tpos++] = A[lpos++];
		else
			TA[tpos++] = A[rpos++];

	while(lpos<=leftend)
		TA[tpos++] = A[lpos++];
	while(rpos<=rightend)
		TA[tpos++] = A[rpos++];

	int i = 0;
	for(i=0;i<numelement;i++,rightend--)
	{
		A[rightend] = TA[rightend];
	}

}
void MSort(elementtype A[],elementtype TA,int left,int right)
{
	int center ;
	if(left < right)
	{
		center = (left+right)/2;
		MSort(A,TA,left,center);
		MSort(A,TA,center+1;right);
		Merge(A,TA,left,center+1,right);
	}
}
void mergesort(elementtype A[],int n)
{
	elementtype *TA;
	TA = (elementtype*)malloc(sizeof(elementtype)); //just malloc once
	if(NULL != TA)
	{
		MSort(A,TA,0,n-1);
		free(TA);
	}
	else
		printf("error: TA can't be empty!\n");
}

头文件里包括其它的一个变量和打印、交换函数

typedef   int elementtype;
#define N 10
//other function used for debug
void print(elementtype A[],int n)
{
	int i = 0;
	printf("after sorting\n");
	for(i=0;i<n;i++)
	{
		printf(" %d \n",A[i]);
	}
}
void swap(elementtype *a,elementtype *b)
{
	elementtype tem = *a;
	*a	= *b;
	*b	= tem;
}

未完待续.......

总结:

 稳定的排序:    插入排序。       冒泡排序,      归并排序

时间复杂度  :        O(n2)  O(n2)
  O(n·lg(n))

不稳定的排序:选择排序,      希尔排序,       堆排。             快排

时间复杂度  :        O(n2)  O(n3/2)
 O(n·lg(n))  O(n·lg(n))

冒泡和插入是慢慢找到最大或最小的放在第一个去;选择直接找到最大或者最小放到第一个。归并、快排都用了devide-merge-conquer(分治策略),期间还是会用到前面提到的那几种最主要的算法;堆排序用了选择排序的思想。桶排序用了空间换时间的方法;万变不离其宗。

參考:

数据结构与算法分析-C语言描写叙述[M],机械工业出版社

博客园 vamei的博客:http://www.cnblogs.com/vamei/archive/2013/03/12/2948847.html

天津城市学院一个精品课程:http://sjjp.tjuci.edu.cn/sjjg/datastructure/ds/web/paixu/paixu8.1.1.1.htm

国外一个排序站点有动画,分析,为代码:http://www.sorting-algorithms.com/

一个在国内不算太有名的国外站点,里面的内容貌似不止算法:

Programming problems and Competitions :: HackerRank

一个俄罗斯的ACM竞赛站点,不定期有算法比赛:

Codeforces

据说是某个Top2大学为后台的算法站点,比較简单,并且一直在定期更新算法新手教程,有定期比赛:

hihoCoder

hdu主办的ACM算法竞赛站点。定期有比赛:

Welcome to BestCoder

宇宙级题库:

UOJ - Universal Online Judge

北大:http://poj.org/

杭电:http://acm.hdu.edu.cn/

华中科技大学:http://acm.hust.edu.cn/vjudge/toIndex.action

时间: 2024-08-11 07:39:56

数据结构与算法分析-排序的相关文章

《数据结构与算法分析:C语言描述》复习——第六章“排序”——冒泡排序

2014.06.17 01:04 简介: 冒泡排序是O(n^2)级别的交换排序算法,原理简单,属于必知必会的基础算法之一. 思路: 排序要进行N轮,每一轮从尾部逐个向前扫描,遇到逆序对就进行交换.确保每一轮把最小的元素交换到前面去.这个过程好比水中的气泡向上飘,所以叫冒泡排序.代码非常简单,所以语言描述反而显得麻烦了. 实现: 1 // My implementation for bubble sort. 2 #include <iostream> 3 #include <vector&

数据结构与算法分析之简单排序算法

在排序算法中,简单排序主要有三种,分别为冒泡排序.选择排序.插入排序,学习理解好这三种排序算法有助于进一步研究数据结构与算法分析.下面,简单地谈一谈冒泡排序.选择排序.插入排序的原理及区别. 冒泡排序原理: 1.比较相邻的元素.如果前一个比后一个大,它们就交换. 2.每对元素都要进行同样的动作,从后往前比较. 3.每趟都会确定一个位置的元素,因此n个元素,需要n-1趟才能确定各个元素的位置. 例如:对23,4,56,11四个数进行冒泡排序. 第一趟 4,23,11,56 第二趟 4,11,23,

数据结构1:数据结构和算法分析

问题引出 假设有一道题目:有一组N个数而要确定其中第k个最大者,我们称之为选择问题,那么这个程序如何编写?最直观地,至少有两种思路: 1.将N个数读入一个数组中,再通过某种简单的算法,比如冒泡排序法,以递减顺序将数组排序,则第k个位置上的元素就是我们需要的元素 2.稍微好一些的做法,将k个元素读入数组并以递减顺序排序,接着将接下来的元素再逐个读入,当新元素被读到时,如果它小于数组中的第k个元素则忽略之,否则将其放到数组中正确的位置上,同时将数组中的一个元素挤出数组,当算法终止时,位于第k个位置上

数据结构:拓扑排序之 确定比赛名词

[BestCoder Round #3 来了!]8月3号19:00~21:00(赛前30分钟停止注册比赛) 确定比赛名次 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total Submission(s): 11642    Accepted Submission(s): 4634 Problem Description 有 N个比赛队(1<=N<=500),编号依次为1,

《数据结构与算法分析—C语言描述》pdf

下载地址:网盘下载 内容简介 编辑 <数据结构与算法分析:C语言描述(原书第2版)>内容简介:书中详细介绍了当前流行的论题和新的变化,讨论了算法设计技巧,并在研究算法的性能.效率以及对运行时间分析的基础上考查了一些高级数据结构,从历史的角度和近年的进展对数据结构的活跃领域进行了简要的概括.由于<数据结构与算法分析:C语言描述(原书第2版)>选材新颖,方法实用,题例丰富,取舍得当.<数据结构与算法分析:C语言描述(原书第2版)>的目的是培养学生良好的程序设计技巧和熟练的算

《数据结构与算法分析》引论:选择问题实现

在<数据结构与算法分析--C语言描述>的引论中有提到一个问题: 设有一组N个数而要确定其中第k个最大者. 被称为选择问题(selection problem). 后面有提到两种算法,下面是我根据描述,写的代码: /* * 来源:<数据结构与算法分析>引论 * 题目:选择性问题,从N个数中找出第k大者 * * */ #include <stdio.h> #include <stdlib.h> /* * 输出数组 * */ void printfArray(in

数据结构与算法分析-索引

作者:xiabodan 出处:http://blog.csdn.net/xiabodan 算法和数据结构是计算机科学的核心内容.作为程序猿,编程是我们的实战项目.然而,写出程序还不够.一个程序在应对一些大型而复杂的情况时.会耗费大量的时间.我们能够非常easy写出一个从文件里找到一个词的程序.比方逐词扫描.看是否相符.但假设我们的文件有几十TB,并且要从文件里找到上百个词,逐个扫描的办法就差点儿不可行.我们须要优化程序,以便我们的程序能够应对复杂问题. 算法研究解决这个问题的方法,而数据结构则是

数据结构与算法分析(4)算法分析的编程练习

    数据结构和算法分析的编程习题.     (1)生成不重复的随机数,并且具有上界和下界.可以产生有序的,也可以产生无序的. 知识: C编程语言不允许返回整个数组作为函数的参数.但是,您也可以返回一个指针,没有索引到一个数组中指定数组的名称. 例如: 1 #include<stdio.h> 2 #include<stdlib.h> 3 #include<time.h> 4 5 int *getRandom(){ 6 static int r[10]; 7 int i

【转】数据结构和算法分析

问题引出 假设有一道题目:有一组N个数而要确定其中第k个最大者,我们称之为选择问题,那么这个程序如何编写?最直观地,至少有两种思路: 1.将N个数读入一个数组中,再通过某种简单的算法,比如冒泡排序法,以递减顺序将数组排序,则第k个位置上的元素就是我们需要的元素 2.稍微好一些的做法,将k个元素读入数组并以递减顺序排序,接着将接下来的元素再逐个读入,当新元素被读到时,如果它小于数组中的第k个元素则忽略之,否则将其放到数组中正确的位置上,同时将数组中的一个元素挤出数组,当算法终止时,位于第k个位置上