第八章 排序

8_3

01、在使用非递归方法实现快速排序时,通常要用一个栈来记忆待排序区间的两个端点。能否用队列 来实现这个栈,为什么?

答:能

栈的作用是在处理一个子区间时,保存另一个子区间的上界和下界(排序过程可能产生新的左右子区间),待该区间处理完后,再从栈中取出另一个子区间的 边界。用队列也可以实现,只是处理子区间的顺序有所变动。

(栈保存相当于先处理最小的区间,再一层层返回,直至大区间;而队列则是逐层逐个区间依次处理)

02、编写双向冒泡排序算法,在正反两个方向交替进行分扫描,即第一趟把关键字最大的元素放在序列的最后面,第二趟把关键字最小的元素放在序列的最前面,如此反复进行。

 //双向起泡
 //思路:
 // 第一趟把最大的元素放在序列的最后面 即从前往后扫描 成功后 修改上界
 // 第二趟把最小的元素放在序列的最前面 即从后往前扫描 成功后 修改下界
 
 void D_BubbleSort(ElemType a[],int n)
 {
 	int low=0,high=n-1;
 	int i,j;
 	bool flag=true;//记录每趟是否交换的标志
	while(low<high&&flag)
	{
		flag=false;
		//将最大的元素放在最后面 
		for(i=low;i<high;i++)
			if(a[i]>a[i+1])
			{
				swap(a[i],a[i+1]);
				flag=true;//交换 则置flag为true 
			}
		high--;//修改上界
		//将最小的元素放在最前面 
		for(i=high;i>low;i--)
			if(a[i-1]>a[i])
			{
				swap(a[i-1],a[i]);
				flag=true;
			} 
		low++;//修改下界 
	} 
}

03、已知线性表按顺序存储,且每个元素都是不相同的的整数型元素,设计把所有奇数移动到所有偶数前边的算法(要求时间最少,辅助空间最少)

//双指针
//low从前往后扫描 high从后往前扫描
//如果a[low]是奇数则low++ a[high]是偶数则high--
//若扫描过程中 出现a[low]是偶数 a[high]是奇数 则交换

void move(ElemType a[],int n)
{
	int low=0,high=n-1;
	while(low<high)
	{
		while(low<high&&a[low]%2==1) low++;//从前往后找到第一个偶数 
		while(low<high&&a[high]%2==0) high--;//从后往前找到第一个奇数
		if(low<high)
		{
			swap(a[low],a[high]);
			low++;high--;//修改指针 继续扫描 
		}
	}
}

04、试重新编写考点精析中的快速排序的划分算法,使之每次选取的枢轴值都是随机地从当前子表中选择的。

//只需在原来代码前加两行
int rand_pos=low+rand%(high-low+1);
swap(a[rand_pos],a[low]); 

05、试编写一个算法,使之能够在数组L(1..n)中找出第k小的元素(即从小到大排序后处于第k个位置的元素)。

#include <iostream>

using namespace std;

typedef int ElemType;

const int N=10010;
int L[N],n,k;


//找出L[1 2 3... n]中第k小的元素

//直接想法:直接对L[]中元素排序 返回L[k] 

//2--王道
//思路:蕴含快排+分治 
//选取一个枢轴元素pivot 使用partition找到枢轴元素在顺序表中的位置pos
//如果 pos==k 则返回pivot
//如果 pos>k 则说明要找的元素在pos左边 即L[1...pos]里 于是在这段区间找第k个位置的元素
//如果 pos<k 则说明要找的元素在pos右边 即L[pos... n]里 于是在这段区间找第k-pos个位置的元素即可 
 

ElemType search_k_th(ElemType L[],int low,int high,int k)
{
	int pivot=L[low];
	int low_pos=low;
	int high_pos=high;//由于下面会修改low 与 high 故先保存 以便递归时使用 
	while(low<high)
	{
		while(low<high&&L[high]>=pivot) high--;//找到比pivot小的元素 放在左边
		L[low]=L[high];
		while(low<high&&L[low]<=pivot) low++;//找到比pivot大的元素 放在右边
		L[high]=L[low]; 
	}
	L[low]=pivot;//将pivot放在最终位置上
	
	if(low==k) //low与k相等 直接返回 
		return L[low];
	else if(low>k)//在前一部分找第k个元素 
		return search_k_th(L,low_pos,low-1,k);
	else
		return search_k_th(L,low+1,high_pos,k);//因为返回的是某个位置的元素 所以这里参数还是传入k 并未k-low 
} 

int main()
{
	cout<<"输入元素个数:";
	cin>>n;
	
	for(int i=1;i<=n;i++) cin>>L[i];
	cout<<"初始元素序列为:";
	for(int i=1;i<=n;i++) cout<<L[i]<<" ";
	cout<<endl;
	
	cout<<"要找的元素序号:";
	cin>>k;
	
	int res=search_k_th(L,1,n,k);
	
	cout<<"第"<<k<<"小的元素为:"<<res<<endl; 
	

	
	return 0;
}

//该算法 时间复杂度为O(n) 

06、荷兰国旗问题:设有一个仅由红、白、蓝三种颜色的条块组成的条块序列,请编写一个时间复杂度为O(n)的算法,使得这些条块按红、白、蓝的顺序排好,即排成荷兰国旗图案。

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

//荷兰国旗问题
//思路:设置三个指针i,j,k;起初i指向线性表第一个元素 k指向线性表最后一个元素 j为工作指针
//j从头到尾扫描一遍线性表 
//如果j为红色 则与i指针所指颜色交换(交换到前面);j为蓝色 则与k指针所指颜色交换(交换到后面);j为白色则j++

typedef enum{
	red,
	white,
	blue
}color;

color a[100];

void swap(color &m,color &n)
{
	color temp=m;
	m=n;
	n=temp;
 } 

void flag_Arrange(color a[],int n)
{
	int i=0,j=0,k=n-1;
	while(j<=k)
	{
		switch(a[j])
		{
			case red:
				swap(a[i],a[j]);
				i++,j++;
				break;
			case white:
				j++;
				break;
			case blue:
				swap(a[j],a[k]);
				k--;//这里不能j++ 因为a[i] 和 a[j]交换后仍可能为blue 
				break;
		}
	}
}

int main()
{
	int n;//元素个数
	char input[20]={0};
	scanf("%d",&n);
	for(int i=0;i<n;i++)
	{
		scanf("%s",input);
		if(strcmp(input,"red")==0)
		{
			a[i]=red;
		}else if(strcmp(input,"white")==0)
		{
			a[i]=white;
		}else if(strcmp(input,"blue")==0)
		{
			a[i]=blue;
		}
		memset(input,0,sizeof(input));
	}
	
	flag_Arrange(a,n);
	
	for(int i=0;i<n;i++)
	{
		switch(a[i])
		{
			case red:
				printf("red ");
				break;
			case white:
				printf("white ");
				break;
			case blue:
				printf("blue ");
				break; 
		}
	}
	 
	
}
 

07、

#include <iostream>
using namespace std;

const int N=10010;

int A[N],A1[N],A2[N];//A1存放A中更大的元素 A2存放A中更小的元素 

//思路:
//要使|n1-n2|最小 同时|S1-S2|最大 则将排序后的前一半元素放入A2(其中元素更小) 后一半元素放入A1(其中元素更大) 
//当n是偶数时刚好一边一半 当n是奇数时则前n/2个元素放入A2(向下取整) 剩余的所有元素放入A1

void Arrange(int A[],int n,int low,int high,int A1[],int A2[])
{//n代表元素个数 low初始为0 high为n-1
	int pos=n/2;
	int flag=1;//标志 若完成了划分 则置flag为0 
	int low0=0,high0=n-1;//初始时 考察范围为[0,n-1] 
	
	//用快排的思想找位置 
	while(flag)
	{
		int pivot=A[low];
		while(low<high)
		{
			while(low<high&&A[high]>=pivot) high--;//从左边找比pivot小的元素 找到放到右边
			A[low]=A[high];
			while(low<high&&A[low]<=pivot) low++;//从右边找比pivot大的元素 找到放在左边
			A[high]=A[low]; 
		}
		A[low]=pivot;
		//出while后 pivot的位置就定了
		if(low==pos-1)//如果low的位置是 目标位置  
		{
			flag=0;//则置flag为0 结束循环 
		}else if(low<pos-1)//若low比目标位置更小 则只需考察low之后的元素 
		{//此时 快排划分的范围变成了[low+1,high0] 
			low0=++low;//修改指针low 为low+1 同时保存low0为下一次划分做准备 因为下一次是更小的范围 
			high=high0;//修改指针high  指向high0 
		}else//若low比目标位置更大 则只需考虑low之前的元素 
		{//此时 快排划分的范围变成[low0,high-1] 
			high0=--high;//修改指针high为high-1 同时改变high0为下一次划分做准备 下一次一定是更小的范围  
			low=low0;//修改指针low 指向low0 
		} 
	}
	for(int i=0;i<pos;i++)  A2[i]=A[i];
	for(int k=0,i=pos;i<n;i++)  A1[k++]=A[i];
} 

int main()
{
	int n;
	cout<<"待划分元素个数:";
	cin>>n;
	
	for(int i=0;i<n;i++) cin>>A[i];
	cout<<"A中元素为:";
	for(int i=0;i<n;i++) cout<<A[i]<<" ";
	cout<<endl;
	
	Arrange(A,n,0,n-1,A1,A2);
	 
	cout<<"A1表中元素为:";
	if(n&1)
	{
		for(int i=0;i<=n/2;i++) cout<<A1[i]<<" ";
	}else
	{
		for(int i=0;i<n/2;i++) cout<<A1[i]<<" ";
	}
	cout<<endl;
	
	cout<<"A2表中元素为:";
	for(int i=0;i<n/2;i++) cout<<A2[i]<<" ";
	
	return 0;
}

8_4

04、编写一个算法,在基于单链表表示的待排序关键字序列上进行简单选择排序。

//基于单链表的简单选择排序
//1---无需断链 每次找到后交换data值
void select_LinkSort(LinkList L)
{
	LNode *p=L->next;//指向首元素结点
	LNode *min,*q; 
	int temp;//保存临时值 
	while(p)
	{
		min=p;
		q=p->next;
		while(q)//寻找最小元素结点 
		{
			if(q->data<min->data)
				min=q;
			q=q->next;
		}
		//找到最小元素结点后 交换两个结点的数据域 
		if(min!=p)
		{
			temp=min->data;
			min->data=p->data;
			p->data=temp;;
		} 
		p=p->next;
	}
	
 } 

//2---断链 每次找到单链表的最大结点 然后将其头插入链表中

LinkList select_LinkSort_2(LinkList L)
{
	LNode *h=L->next,*pre,*p,*maxpre,*max;
	L->next=NULL;//将L从头结点断开 方便头插
	
	//直到h扫描到链表尾 即NULL 
	while(h)
	{
		pre=maxpre=NULL,p=max=h;
		
		//找到最大元素结点 
		while(p)
		{
			if(p->data>max->data)
			{
				max=p;
				maxpre=pre;
			}
			pre=p;
			p=p->next;
		
		}
			
		if(max==h)//最大结点 为首元素结点 
			h=h->next;//直接让h后移
		else//否则 将该元素结点摘除 保证链表连接 
			maxpre->next=max->next;	
		 
		//头插
		max->next=L->next;
		L->next=max;  
	} 
	
	return L;
	
 }

05、试设计一个算法,判断一个数据序列是否构成一个小根堆。

//判断一个数据序列是否构成小根堆(用顺序表存储堆 且视为完全二叉树)

//思路:与建堆类似 判断len/2的左右孩子(如果有的话) 然后依次上升到第一个位置 
bool Judge_minHeap(ElemType a[],int len)
{
	if(len%2==0)//结点个数为偶数 有一个单分支结点 
	{
		if(a[len/2]>a[len])
			return false;
		for(int i=len/2-1;i>=1;i--)
		{
			if(a[i]>a[2*i]||a[i]>a[2*i+1])//若父母结点大于左孩子或者右孩子结点 
				return false;//则不是小根堆 
		}
	}else//结点个数为奇数 则每个分支节点都有左右孩子 
	{
		for(int i=len/2;i>=1;i--)
		{
			if(a[i]>a[2*i]||a[i]>a[2*i+1])
				return false;
		}
	}
	return true;
 }

8_6

02、

//1--插入排序

void Insert_Sort(ElemType a[],int m,int n)
{
	int i,j;
	for(i=m+1;i<=m+n;i++)
	{
		a[0]=a[i];
		for(j=i-1;a[j]<a[0];j--)
			a[j+1]=a[j];
		a[j+1]=a[0];
	}
	
 } 
 
//最坏情况下 元素比较次数O(mn) 移动次数O(mn) 时间复杂度O(mn)
//空间复杂度 O(1)

//2--归并排序
ElemType  *b=(ElemType *)malloc((m+n+1)*sizeof(ElemType));
void MergeSort(ElemType a[],int m,int n)
{
	int i,j,k;
	for(int k=0;k<m+n;k++) b[k]=a[k];
	
	for(i=0,j=m,k=0;i<m&&j<n;k++)
	{
		if(b[i]<=b[j]) 
			a[k]=b[i++];
		else
			a[k]=b[j++];
	} 
	
	while(i<m) a[k++]=b[i++];
	while(j<n) a[k++]=b[j++];
 } 
 
//时间复杂度:O(m+n)  空间复杂度:O(m+n) 

03、

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

typedef int ElemType;

//计数排序

void Count_Sort(ElemType a[],int n)//n表示有n个元素
{
	ElemType *b=(ElemType *)malloc((n+1)*sizeof(ElemType));
	int cnt=0;//记录小于关键码的元素个数
	int i,j;//工作指针 
	for(i=0;i<n;i++)
	{
		int key=a[i];
		for(j=0;j<n;j++)
		{
			if(a[j]<key)
				cnt++;
		}
		b[cnt]=key;
		cnt=0;//一轮循环后 cnt置0 
	} 
	for(i=0;i<n;i++) a[i]=b[i];//拷贝回原数组 
 } 
 
int main()
{
	int n;
	scanf("%d",&n);
	int a[n];
	
	printf("排序前:");
	getchar();
	for(int i=0;i<n;i++)
	{
		scanf("%d",&a[i]);
		getchar();
	}
	printf("\n");
	Count_Sort(a,n);
	printf("排序后:");
	for(int i=0;i<n;i++) printf("%d ",a[i]);
	
	
	return 0;
}

//比较次数 n² 

//简单选择排序比本排序号 简单选择排序只比较 n*(n-1)/2


//若限制任意两个记录只比较一次

const int N=10010;

int a[N],b[N],s[N];//s[N]作为计数器 

void Count_Sort_2(ElemType a[],int n)
{
	int i,j;
	for(i=0;i<n;i++)
	{
		for(j=i+1;j<n;j++)
		{
			if(a[i]<a[j])//若当前元素比后序扫描的元素小
				s[a[j]]++; //则让扫描到的元素的计数域+1 
			else
				s[a[i]]++;//否则 让当前元素的计数域+1
		}
		b[s[a[i]]]=a[i];
	}
	for(int i=0;i<n;i++) a[i]=b[i];
 

04、设有一个数组中存放了一个无序的关键序列K1,K2,...Kn。现要求将Kn放在将元素排序后的正确位置上,试编写实现该功能的算法,要求比较关键字的次数不超过n。

//以Kn为枢轴元素进行一次快排
int Partition(ElemType a[],int high,int low)
{
	low=0,high=n;
	int pivot=a[high];
	while(low<high)
	{
		while(low<high&&a[low]<=pivot) low++;//从前往后找比pivot大的元素 找到则交换 
		a[high]=a[low];
		while(low<high&&a[high>=pivot]) high--;//从后往前找比pivot小的元素  找到则交换 
		a[low]=a[high];
	}
	
	a[low]=pivot;//将枢轴元素放在最终位置上 
	return low; 
} 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值