数据结构与算法 第11部分:排序

1:直接插入排序

算法步骤:
1)设待排度的记录存储在数组r[1...n]中,可以把第一个记录r[1]看作一个有序序列。
2)依次将r[i](i = 2,…,n)插入到已经排好序的序列r[1..i - 1]中,并保持有序性。

void StraightInsertSort(int r[],int n)  //直接插入排序
{
	for (int nIndex = 2; nIndex <= n; nIndex++)  //r[nIndex]插入有序子表
	{
		if (r[nIndex] >= r[nIndex - 1]) //r[nIndex]和前一个元素r[nIndex-1]比较
		{
			continue;
		}
		r[0] = r[nIndex];			//r[nIndex]暂存到r[0]中,r[0]有监视哨的作用
		r[nIndex] = r[nIndex - 1];	    //r[nIndex-1]后移一位
		for (int j = nIndex - 2; r[j] > r[0]; j--) //从后向前寻找插入位置,逐个后移,直到找到插入位置
		{
			r[j + 1] = r[j];		//r[j]后移一位
		}			
		r[j + 1] = r[0];		//将r[0]插入到r[j+1]位置
	}
}

2:冒泡排序

冒泡排序是一种最简单的交换排序算法,通过两两比较关键字,如果逆序就交换,
使关键字大的记录像泡泡一样冒出来放在尾部。重复执行若干次冒泡排序,最终得到有序序列。

算法步骤:
1)设待排度的记录存储在数组r[1..n]中,首先第一个记录和第二个记录关键字比较,若逆序则交换;
然后第一个记录和第二个记录关键字比较,…,以此类推,直到第n - 1个记录和第n个记录关键字比较完毕为止。
第一趟排序结束,关键字最大的记录在最后一个位置。
2)第二趟排序,对前n - 1个元素进行冒泡排序,关键字次大的记录在n - 1位置。
3)重复上述过程,直到某一趟排序中没有进行交换记录为止,说明序列已经有序。

void BubbleSort(int r[],int n) //冒泡排序
{
	for (int nIndex = n - 1; nIndex > 0; nIndex--)
	{
		for (int i = 0; i < nIndex; i++) //进行一趟排序
		{
			if (r[i] <= r[i + 1])
			{
				continue;
			}
			int temp = r[i]; //交换两个记录
			r[i] = r[i + 1];
			r[i + 1] = temp;
		}
	}
}

3:快速排序

算法步骤:
1)首先取数组的第一个元素作为基准元素pivot = R[low]。i = low,j = high。
2)从右向左扫描,找小于等于pivot的数,如果找到,R[]和R]交换,i++。
3)从左向右扫描,找大于pivot的数,如果找到,R[]和R]交换,j--。
4)重复步骤2~步骤3,直到i和指针重合,返回该位置mid - i,该位置的数正好是pivot元素。
5)至此完成一趟排序。此时以mid为界,将原数据分为两个子序列,左侧子序列元素
都比pivot小,有侧子序列元素都比pivot大,然后再分别对这两个子序列进行快速排序。

int Partition(int r[],int low,int high)//划分函数
{
    int i = low,j = high,pivot = r[low];//基准元素
    while(i < j)
    {
        while (i<j && r[j]>pivot)
        {
            j--;//向左扫描找到第一个比基准元素大的
        }
        if(i<j)
        {
            swap(r[i++],r[j]);//r[i]和r[j]交换后i+1右移一位
        }

        while(i<j && r[i] <= pivot) 
        {
            i++;//向右扫描找到第一个比基准元素小的
        }

        if(i<j)
        {
            swap(r[i],r[j--]);//r[i]和r[j]交换 后j-1左移一位
        }
    }
    return i;//返回最终划分完成后基准元素所在的位置
}

算法改进:
从上述算法可以看出,每次交换都是在和基准元素进行交换,实际上没必要这样做
我们想把原序列分成以基准元素为界的两个子序列,左侧子序列小于等于基准元素,右侧子序列大于基准元素。
我们可以从右向左扫描,找小于等于pivot的数R[j],然后从左向右扫描,找大于pivot的数R[i],让R[i]和R[j]交换,
一直交替进行,直到i和j碰头为止,这时将基准元素与R[i]交换即可。这样就完成了一次划分过程,但交换元素的个数少了很多。

int Partition2(int r[],int low,int high)//划分函数
{
    int i = low,j = high,pivot = r[low];//基准元素
    while(i<j)
    {
        while (i<j && r[j]>pivot)
        {
            j--;//向左扫描找到第一个比基准元素大的
        }
        while (i < j && r[i] <= pivot)
        {
            i++;//向右扫描找到第一个比基准元素小的
        }
        if(i<j)
        {
            swap(r[i++],r[j--]);//r[i]和r[j]交换
        }
    }
    if(r[i]>pivot)
    {
        swap(r[i-1],r[low]);//r[i-1]和r[low]交换
        return i-1;//返回最终划分完成后基准元素所在的位置
    }
    swap(r[i],r[low]);//r[i]和r[low]交换
    return i;//返回最终划分完成后基准元素所在的位置
}
void QuickSort(int R[],int low,int high)//实现快排算法
{
    if(low >= high)
    {
		return;
    }
    int mid = Partition(R, low, high); //基准位置
    QuickSort(R, low, mid - 1);//左区间递归快排
    QuickSort(R, mid + 1, high);//右区间递归快排
}

4:合并排序

算法设计:合并排序是采用分治策略实现对n众元素进行排序的算法:
(1)分解一一将待排序元素分成大小大致相同的两个子序列。
(2)治理——对两个子序列进行合并排序。
(3)合并一一将排好序的有序子序列进行合并,得到最终的有序序列。

void Merge(int A[], int low, int mid, int high)//合并函数
{
    int *B = new int[high-low+1];//申请一个辅助数组
    int i=low, j=mid+1, k=0;
    while(i<=mid && j<=high) //按从小到大存放到辅助数组B[]中
    {
        B[k++] = (A[i] <= A[j]) ? A[i++] : A[j++];
    }
    //将数组中剩下的元素放置B中
    while (i <= mid)
    {
        B[k++] = A[i++];
    }
    while (j <= high)
    {
        B[k++] = A[j++];
    }

    for (i = low, k = 0; i <= high; i++)
    {
        A[i] = B[k++];
    }      
    delete []B;//释放空间
}

void MergeSort(int A[], int low, int high)//合并排序
{
    if(low >= high)
    {
		return;
    }
    int mid = (low + high) / 2;//取中点
    MergeSort(A, low, mid);//对A[low:mid]中的元素合并排序
    MergeSort(A, mid + 1, high);//对A[mid+1:high]中的元素合并排序
    Merge(A, low, mid, high);//合并
}

5:选择排序

算法步骤:每次从待排序序列中选择一个最小的放在最前面。
1)设待排序的记录存储在数组r[1..n]中,首先从r[1..n]中选择一个关键字最小的记录
r[k],r[k]与r[1]交换。
2)第二趟排序,从r[2..n]中选择一个关键字最小的记录r[k],r[k]与r[2]换。
3)重复上述过程,经过n - 1趟排序,得到有序序列。

void SelectSort(int r[],int n) //选择排序
{
    for(int i = 0 ; i < n-1 ; i++)//n-1趟排序
    {
        int k = i;
        for (int j = i + 1; j < n; j++)//找最小值
        {
            if (r[j] >= r[k])
            {
                continue;
            }
            k = j;   //记录最小值下标
        }

        if(k != i)
        {
            int temp = r[i];// r[i]与r[k]交换
            r[i] = r[k];
            r[k] = temp;
        }
    }
}

6:堆排序

算法步骤:
1)构建初始堆。
2)堆顶和最后一个记录交换,即r[1]和r[n]交换,将r[1..n - 1]重新调整为堆。
3)堆顶和最后一个记录交换,即r[1]和r[n - 1]交换,将r[1..n - 2]重新调整为堆。
4)循环n - 1次,得到一个有序序列。

“下沉”操作:堆顶与左右孩子比较,如果比孩子大,则已调整为堆;如果比孩子小,
则与较大的孩子交换,交换到新的位置后,继续向下比较,从根结点一直比较到叶子。

void Sink(int k,int n)//下沉操作
{
    while(2*k <= n)//如果有左孩子
    {
        int j = 2*k;//j指向左孩子
        if (j < n && r[j] < r[j + 1])//如果有右孩子且左孩子比右孩子小
        {
            j++;    //j指向右孩子
        }
            
        if (r[k] >= r[j])//比较大的孩子大
        {
            break;    //已满足堆
        }
        else
        {
            swap(r[k], r[j]);//与较大的孩子交换
        }           
        k=j;//k指向交换后的新位置,继续向下比较,一直下沉到叶子
    }
}

void CreatHeap(int n)//构建初始堆
{
	for (int i = n / 2; i > 0; i--)//从最后一个分支结点n/2开始调整为堆,直到第一个结点
	{
		Sink(i, n);
	}     
}

void HeapSort(int n)//堆排序
{
    CreatHeap(n);//构建初始堆
    while(n > 1)
    {
        swap(r[1],r[n--]);//堆顶和最后一个记录交换,交换后n减1
        Sink(1,n);//堆顶下沉
    }
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序员的资料库

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值