排序(选择)

选择排序基本思想:每一趟(如第i趟)在后面n-i+1个待排序元素中选取关键字最小的元素,作为有序子序列的第i个元素,直到n-1趟昨晚,待排序元素只剩下1个,就不用再选了。

           人话:有一堆数,以升序为例子,第一次从这些数中找到最小的放在第一个位置,第二次从剩下的中找到最小的放在之前排好数后面,以此类推,直到待排序的数一个也没了。

              简记:每次找最小,挨着放

目录

一、简单选择排序

二、堆排序⭐⭐⭐⭐

三 、锦标赛排序


一、简单选择排序

       (又名直接选择排序)基本来自选择排序的思想,

         基本步骤:(1)选出最小的

                           (2)若它不是第一个,则与第一个交换位置 

                           (3)去除之前选出的最小与那素中,继续执行(1)(2),直到剩余元素只有一个为止

执行流程:

                  初始序列{49,38,65,97,76,13,27} 

                 在排序过程中,把序列分为有序部分和无序部分。开始时,整个序列为无序序列。

                                                                  无序
49386597761327

第一趟,从无序序列选出最小元素13,它与无序序列中第一个元素交换

有序无序
13386597764927

第二趟,从无序序列选出最小元素27,它与无序序列中第一个元素交换

有序无序
13276597764938

重复上述操作,直到无需序列没有元素
性能:空间效率:仅使用常数个辅助单元,为O(1) 

           时间效率:为O(n^2)

          稳定性:在第i趟找到最小元素后,和第i个元素交换,会导致第i个元素与其含有相同关键字元素的相对位置发生改变。

                         所以不稳定

#include<stdio.h>
#include<stdlib.h>

void swap(int *a,int *b)
{
	*a ^= *b;
	*b ^= *a;
	*a ^= *b;
}
void SelectSort(int arr[],int n)
{  //arr进行排序,arr从0开始存放元素 
	int i,j;
	for(i=0;i<n-1;i++){//一共进行n-1趟排序 
		int min=i;
		for(j=i+1;j<n;j++)    //再arr[i..n-1]中选择最小元素 
			if(arr[j]<arr[min]) min=j;//更新最小元素位置
		if(min!=i) 
			swap(&arr[i],&arr[min]);	//与第i个交换位置 
	}
}

void put(int arr[],int n){
	int j;
	for(j=0;j<n;j++)
	printf("%d\t",arr[j]);
	printf("\n");
}
void main(){
	int array[]={0,4,2,1,4,2,8,3,1,10}; 
	int i;
	int len=sizeof(array)/sizeof(array[0]);
	printf(" \n排序前\n");
	put(array,len);
	SelectSort(array,len);
	printf(" \n排序后\n");
	put(array,len);
} 

二、堆排序⭐⭐⭐⭐

利用堆及其运算,可实现选择排序。

堆排序步骤为:(1)根据初始输入数据,利用堆的调整算法,形成初始堆

                         (2)通过一系列的元素交换和重新调整堆进行排序

                       具体为:把大根堆对顶的最大数取出,将剩余的堆继续调整为大根堆,再次将堆顶的最大数取出,这个过程持续                           到剩余数只有一个时结束

为了实现元素从小达到排序,所以要建立大根堆。(小根堆与之类似)

大根堆的定义:

  • 大根堆中的最大元素值出现在根结点(堆顶)

  • 堆中每个父节点的元素值都大于等于其孩子结点

执行流程:

(1)建堆,先将序列调整为一个大顶堆。原始序列对应的完全二叉树如图

在这个完全二叉树中,结点76、13、27、49* 是叶子结点,它们没有左孩子,所以它们满足堆的定义。

从97开始,按87、65、48、49的顺序依次 调整。

1.建堆

(1)调整97。97>49*,所以97和它的孩子49*满足堆的定义,所以不需要调整

(2)调整65。65>13,65>27,所以65和它的孩子13、27满足堆的定义,不需要调整。

(3)调整38。38<97,38<76,不满足堆定义,需要调整。在这里38的两个孩子都比38大,应该选择更大的97交换。因为,若和76交换,则76<97仍然不满足堆的定义。

         因此,将38和97交换。交换后38成了49*的根结点,38<49*,仍然不满足堆定义,需要继续调整,将48和49*交换得到如图所示

(4)调整49.49<97,49<65不满足堆的定义,需要调整,找到较大的孩子97,将49和97交换。

         交换后49<76仍不满足堆定义,继续调整,将49与76交换,得到如图

2.插入结点

3.删除结点

4.排序

此时已经建立好一个大根堆,对应的序列为97 ,76 ,65  , 49* ,49 ,13 ,27 ,38

将堆顶关键字97和序列最后一个关键字38交换。第一趟排序完成,97到达最终位置。

将除97外的序列38,76,65,49*,13,27,重新调整为大根堆。

现在只有38不满足堆定义,其他的关键字都满足,只需调整38,结果如图

现在序列为 76 ,49*,65,38,49,13,27.将堆顶关键字76和最后一个关键字27交换,第二趟排序完成。

76到达其最终位置,此时序列为 27 49* 65 38 46 13 76 97

堆除去76和977的序列依照上述方法进行处理,直到树中只有一个结点时完成。

 

性能分析:时间复杂度  平均为O(nlog_{2}n)

                空间复杂度  O(1)

                 稳定性:不稳定

#include <stdio.h>
#include <stdlib.h>
 
void swap(int *a,int *b)
{
	*a ^= *b;
	*b ^= *a;
	*a ^= *b;
}
 
void max_heapify(int arr[], int start, int end) 
{
    //建立父节点指标和子节点指标
    int dad = start;
    int son = dad * 2 + 1;
    while (son <= end)  //若子节点指标在范围内才做比较
        {
            if (son + 1 <= end && arr[son] < arr[son + 1]) 
            //先比较两个子节点大小,选择最大的
            son++;
        if (arr[dad] > arr[son]) //如果父节点大於子节点代表调整完毕,直接跳出函数
            return;
        else  //否则交换父子内容再继续子节点和孙节点比较
        {
            swap(&arr[dad], &arr[son]);
            dad = son;
            son = dad * 2 + 1;
        }
    }
}
 
void heap_sort(int arr[], int len) 
{
    int i;
    //初始化,i从最後一个父节点开始调整
    for (i = len / 2 - 1; i >= 0; i--)
        max_heapify(arr, i, len - 1);
    //先将第一个元素和已排好元素前一位做交换,再重新调整,直到排序完毕
    for (i = len - 1; i > 0; i--) 
    {
        swap(&arr[0], &arr[i]);
        max_heapify(arr, 0, i - 1);
    }
}
 
int main() {
    int arr[] = { 3, 5, 3, 0, 8, 6, 1, 5, 8, 6, 2, 4, 9, 4, 7, 0, 1, 8, 9, 7, 3, 1, 2, 5, 9, 7, 4, 0, 2, 6 };
    int len = (int) sizeof(arr) / sizeof(*arr);
    heap_sort(arr, len);
    int i;
    for (i = 0; i < len; i++)
        printf("%d ", arr[i]);
    printf("\n");
    return 0;
}

三 、锦标赛排序

        锦标赛排序,也称为树形选择排序(Tree Selection Sort),是一种按照锦标赛的思想进行选择排序的方法。

  首先对n个记录进行两两比较,然后优胜者之间再进行两两比较,如此重复,直至选出最小关键字的记录为止。这个过程可 以用一棵有n个叶子结点的完全二叉树表示。根节点中的关键字即为叶子结点中的最小关键字。在输出最小关键字之后,根据关系的可传递性,欲选出次小关键字,
仅需将叶子结点中的最小关键字改为“最大值”,如∞,然后从该叶子结点开始,和其左(右)兄弟的关键字进行比较,修改从叶子结点到根的路径上各结点的关键 字,则根结点的关键字即为次小关键字。

  这种算法的缺点在于:辅助存储空间较多、最大值进行多余的比较。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值