您的位置:首页 > 理论基础 > 数据结构算法

数据结构--排序总结

2017-12-04 11:51 267 查看
二、插入排序

(一) 思想:在一个已经排好序的序列中,将未被排进的元素按照原先的规定插入到指定位置

1、直接插入排序

直接插入排序基本要求:假设待排序的记录存放在数组r[0…n-1].开始时,先将r[0]组成一个有序子表,然后依次将后面的记录插入到这个子表中,并一直保持子表的有序性。

1.1 直接插入排序的主要步骤:

(1)将r[i]暂存在临时变量temp中

(2)将temp与rj依次比较,若r[j].key>temp.key,则将r[j]后移一个位置,直到temp.key>=r[j].key为止,

(3)将temp插入到j+1的位置上

(4)令i=1,2,3,..,n-1,重复以上步骤1-3

1.2

(1)时间复杂度:T(n) = O(n²)。

(2)空间复杂度:S(n) = O(1)。

(3)稳定性:稳定排序。循环条件while(r[0].key < r[j].key)保证的。

(4)程序:

public void insertSort(RecordType r[], int length)
{
for (i=2;i<=length;i++)
{
r[0]=r[i];//将r[0]设为监视哨
for(j=i-1;r[0].key<r[j].key;j--)//r[0]==r[0]即为j的终止条件
{
r[j+1]=r[j];
}
r[j+1]=r[0];
}
}


2、折半查找法

(1)思想:类比于直接插入法,在进行插入的时候不同于直接插入法对已排好序的元素依次比较,而是进行折半比较,减少比较次数。

(2)时间复杂度:比较时的时间减为O(n㏒n),但是移动元素的时间耗费未变,所以总是得时间复杂度还是O(n²)。、

(3)空间复杂度:S(n) = O(1)。

(4)稳定性:稳定排序。

(5)程序:

void BinSort(RecordType r[], int length)
{
for(i=2;i<=length;i--)//i为待插入元素的位置
{
r[0]=r[i]//将r[0]设为监视哨
low=1;
high=i-1;
while(low<=high)//通过while找到要插入的位置为low
{
mid=(low+high)/2;
if (r[0].key<r[mid].key)
high=mid-1;
else
low=mid+1;
}//最终low的位置就是要插入数值的位置
for(j=i-1;j>=low;j--)
{
r[j+1]=r[j];
}
r[low]=r[0];

}


3、希尔排序

① 思想:又称缩小增量排序法。把待排序序列分成若干较小的子序列,然后逐个使用直接插入排序法排序,最后再对一个较为有序的序列进行一次排序,主要是为了减少移动的次数,提高效率。原理应该就是从无序到渐渐有序,要比直接从无序到有序移动的次数会少一些。

② 时间复杂度:O(n的1.5次方)

③ 空间复杂度:O(1)

④ 稳定性:不稳定排序。{2,4,1,2},2和1一组4和2一组,进行希尔排序,第一个2和最后一个2会发生位置上的变化。

⑤ 程序:

public void shellSort(int[] d){//d[]为增量数组
RecordNode temp;
int i,j;
//对增量数组中的每个值进行扫描
for (int
bcd0
k=0;k<=d.length;k++){
//一趟中若干子表,每个记录在自己所属的子表内进行直接插入排序
int dk=d[k];
//r待排序的数组中前dk个元素分别属于dk增量下的dk个子表中的首元素
for(i=dk;i<this.curlen;i++){
temp=r[i];
for(j=i-dk;j>=0&&temp.key<r[j].key;j-=dk){
r[j+dk]=r[j];
}
r[j+dk]=temp;
}
}
}


三、交换排序

(1)思想

两两待排序记录的关键字,若两个记录的次序相反则交换这两个记录,直到没有反序的记录为止,交换排序的主要方法有:冒泡排序和快速排序

1、 冒泡排序:

① 思想:反复扫描待排序序列,在扫描的过程中顺次比较相邻的两个元素的大小,若逆序就交换位置。第一趟,从第一个数据开始,比较相邻的两个数据,(以升序为例)如果大就交换,得到一个最大数据在末尾;然后进行第二趟,只扫描前n-1个元素,得到次大的放在倒数第二位。以此类推,最后得到升序序列。如果在扫描过程中,发现没有交换,说明已经排好序列,直接终止扫描。所以最多进行n-1趟扫描。

② 时间复杂度:T(n) = O(n²)。

③ 空间复杂度:S(n) = O(1)。

④ 稳定性:稳定排序。

⑤ 程序:

public void bublleSort()
{
RecordNode temp;//辅助节点
boolean flag=true;//是否交换标志
for(i=1;i<this.curlen&&flag;i++)
{
flag=false;//记录未交换
for(j=0;j<this.curlen-i;j++)
{
if(r[j]>r[j+1])
{
temp=r[j+1];
r[j+1]=r[j];
r[j]=temp;
flag=true;
}
}
}
}


2、快速排序

2、 快速排序:

① 思想:冒泡排序一次只能消除一个逆序,为了能一次消除多个逆序,采用快速排序。以一个关键字为轴,从左从右依次与其进行对比,然后交换,第一趟结束后,可以把序列分为两个子序列,然后再分段进行快速排序,达到高效。

② 时间复杂度:平均T(n) = O(n㏒n),最坏O(n²)。

③ 空间复杂度:S(n) = O(㏒n)。

④ 稳定性:不稳定排序。{3, 2, 2}

⑤ 程序:

void QKSort(RecordType r[],int i, int j)
{
int pos;
if(i<j){
pos=QKPass(r,i,j);
QKSort(r,i,pos-1);
QKSort(r,pos+1,j);
}
}
int QKPass(RecordType r[],int i, int j)
{
RecordNode pivot=r[i];
while(i<j){
while(i<j&&pivot<=r[j]){
j--;
}
if(i<j){
r[i]=r[j];
i++;
}
while(i<j&&pivot>r[i]){
i++;
}
if(i<j){
r[j]=r[i];
j--;
}
}
r[i]=pivot;
return i;
}


四、选择类排序:

(一) 思想:每一趟在n – i + 1 ( i = 1,2, … , n - 1)个记录中选取关键字最小的记录作为有序序列中的第i个记录。

(二) 分类:

1、 简单选择排序:

① 思想:第一趟时,从第一个记录开始,通过n – 1次关键字的比较,从n个记录中选出关键字最小的记录,并和第一个记录进行交换。第二趟从第二个记录开始,选择最小的和第二个记录交换。以此类推,直至全部排序完毕。

② 时间复杂度:T(n) = O(n²)。

③ 空间复杂度:S(n) = O(1)。

④ 稳定性:不稳定排序,{3, 3, 2}。

⑤ 程序:

public void selectSort(){
RecordNode temp;
for(int i=0;i<this.curlen;i++){//n-1趟排序
//每趟从r[i]开始的子序列中寻找最小关键字值得记录
int min=i;
for(int j=i+1;j<this.curlen;j++){
//在子序列中寻找关键字最小的记录
if(r[j]<r[min])
min=j;
}
if(min!=i){
temp=r[i];
r[i]=r[min];
r[min]=temp;
}
}
}


2、 树形选择排序:

① 思想:为了减少比较次数,两两进行比较,得出的较小的值再两两比较,直至得出最小的输出,然后在原来位置上置为∞,再进行比较。直至所有都输出。

② 时间复杂度:T(n) = O(n㏒n)。

③ 空间复杂度:较简单选择排序,增加了n-1个额外的存储空间存放中间比较结果,就是树形结构的所有根节点。S(n) = O(n)。

④ 稳定性:稳定排序。

⑤ 程序:

3、 堆排序:

① 思想:把待排序记录的关键字存放在数组r[1…n]中,将r看成是一刻完全二叉树的顺序表示,每个节点表示一个记录,第一个记录r[1]作为二叉树的根,一下个记录r[2…n]依次逐层从左到右顺序排列,任意节点r[i]的左孩子是r[2i],右孩子是r[2i+1],双亲是r[i/2向下取整]。然后对这棵完全二叉树进行调整建堆。

② 时间复杂度:T(n) = O(n㏒n)。

③ 空间复杂度:S(n) = O(1)。

④ 稳定性:不稳定排序。{5, 5, 3}

⑤ 程序:

(1) 筛选法调整堆:

由堆的定义可知,堆顶结点的左右子树也是堆,当将堆顶最小关键字值得节点与堆最后一个节点(第n-1个节点)交换后,这个根节点与其左右子树之间的可能不符合堆得定义,此时需要调整根节点与其左右两个堆顶节点之间的大小关系,使之符合定义。

调整方法:将根节点与左右孩子中较小的节点进行交换,继续对交换的子树进行相同的操作。直到叶子节点或堆被建成为止,这种从堆顶到叶子结点的调整过程也称为“筛选”

//将以low为根节点的字数调整成小顶堆,low和high分别是序列的下界和上界
public void sift(int low, int high){
int i=low; //子树为根节点
int j=2*i;//j为i的左孩子
RecordNode temp=r[i];
while(j<high){
if(j<high-1&&r[j]>r[j+1])
j++;//记录比较,j为左右孩子的较小者
if(temp.key>r[j]){//若父节点较大
r[j]=r[i];//孩子节点中较小者上移
i=j;
j=2*j+1;
}else{
j=high+1;
}
}
r[i]=temp;
}


(2)堆排序算法

public void heapSort(){
System.out.println("堆排序");
int n=this.curlen;
RecordNode temp;
//创建堆,即从第一个非叶子节点开始自下而上调整堆;
for(int i=n/2-1;i>=0;i--){
sift(i,n);
}
for(int i=n-1;i>0;i--){
//每趟将最小关键字交换到后面,在调整成堆
temp=r[0];
r[0]=r[i];
r[i]=temp;
sift(0,i);
}
}


五、归并排序:

(一) 思想:

(二) 分类:

1、 归并排序:

① 思想:假设初始序列有n个记录,首先将这n个记录看成n个有序的子序列,每个子序列的长度为1,然后两两归并,得到n/2向上取整 个长度为2(n为奇数时,最后一个序列的长度为1)的有序子序列。在此基础上,在对长度为2的有序子序列进行两两归并,得到若干个长度为4的有序子序列。如此重复,直至得到一个长度为n的有序序列为止。

② 时间复杂度:T(n) = O(n㏒n)。

③ 空间复杂度:S(n) = O(n)。

④ 稳定性:稳定排序。

⑤ 程序:

//两个有序序列归并算法把r数组中两个相邻的有序表r[h]-r[m]和r[m+1]-、r[t]归并为一个有序表order[h]-order[t]
public void merge(RecordNode[] r,RecordNode[] order, int h, int m, int t){
int i=h,j=m+1,k=h;
while(i<=m&&j<=t){
//将r中的两个相邻子序列归并到ORDER中
if(r[i].key<r[j].key){//将较小者复制到order中
order[k++]=r[i++];
}else{
order[k++]=r[j++];
}
while(i<=m){//将前一个子序列剩余元素复制到order中
order[k++]=r[i++];
}
while(j<=t){
order[k++]=r[j++];//将后一个子序列剩余元素复制到order中
}
}
}


//一趟归并排序
public void mergepass(RecordNode[] r,RecordNode[] order,int s,int n){
int p=0; //P为每一位待合并表的第1个元素的下标,初值为0
while(p+2*s-1<=n-1){//两两归并长度为s的有序表
merge(r,order,p,p+s-1,p+2*s-1);
p=2*s;
}
if(p+s-1<n-1)//n-1为最后一位元素,说明此事还有两个有序表{
merge(r,order,p,p+s-1,n-1);
}else{
for(int i=p;i<=n-1;i++){
order[i]=r[i];
}
}
}


//二路归并排序算法
public void mergeSort(){
System.out.println("归并排序");
int s=1;
int n=this.curlen;
RecordNode[] temp=new RecordNode
;//定义长度为n的辅助数组temp
while(s<n){
mergepass(r,temp,s,n);//一趟归并,将r数组中各子序列归并到temp中
display();
s*=2;
mergepass(temp,r,s,n);//将temp数组中各子序列归并到r中
display();
s*=2;
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: