【数据结构与算法】- 排序(算法)

目录

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

二:已知线性表按顺序存储,且每个元素都是不相同的整数型元素,设计把所有奇数移动到所有偶数前面的算法

三:试着重新编写 快速排序的划分算法,使之每次选取的枢轴值都是随机地从当前子表中选择的

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

五:荷兰国旗问题:设有一个仅由红,白,蓝三种颜色的条块组成的条块序列,请编写算法,使得这些条块按红 白 蓝的顺序排好,即排成荷兰国旗图案

六:已知由n(n>=2)个正整数构成的集合A={ak|0<=k},将其划分成为两个不相交的子集a1和a2。元素个数分别是n1和n2,a1和a2中的元素之和分别为s1和s2,设计一个算法,满足|n1-n2|最小且|s1-s2|最大

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

八:试着设计一个算法,判断一个数据序列是否构成一个小根堆

九:设顺序表用数组A[ ]表示,表中元素存储在数组下标1 -  m+n范围内,前m个元素递增有序,后n个元素递增有序,设计一个算法,使得整个顺序表有序

十:有一种简单的排序算法,称为计数排序,这种排序算法对一个待排序的表(用数组表示)进行排序,并将排序结果存放到另一个新的表中,必须注意的是,表中所有待排序的关键码互不相同,计数排序算法针对表中的每个记录,扫描待排序的表一趟,统计表中有多少个记录的关键码比该记录的关键码小,假设针对某个记录统计出的计数值为c,则这个记录在新有序表中的合适存放位置为c

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


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

#include<iostream>
using namespace std;

//双向冒泡
void bsort(int a[],int n)
{
	int i;
	//范围的左右两端
	int low=0,high=n-1;
	//一趟是否交换过 初始化
	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;
			}
		}
		high--;
		//从后向前交换
		for(i=high;i>low;i--)
		{
			if(a[i]<a[i-1])
			{
				swap(a[i],a[i-1]);
				flag  = true;
			}
		}
		low++;
	}
}

int main()
{
	int a[10] = {7,5,9,2,6,4,3,8,1};
	bsort(a,9);
	for(int i=0;i<9;i++)
	{
		cout<<a[i]<<" ";
	}
	return 0;
}

二:已知线性表按顺序存储,且每个元素都是不相同的整数型元素,设计把所有奇数移动到所有偶数前面的算法

#include<iostream>
using namespace std;

void Move(int a[],int n)
{
	//查找范围的左右两端
	int i=0,j=n-1;
	//循环遍历条件
	while(i<j)
	{
		//从前向后找偶数位置
		while(i<j&&a[i]%2==1) i++;

		//从后向前找奇数位置
		while(i<j&&a[j]%2==0) j--;
		//若偶数在奇数前面 交换
		if(i<j)
		{
			//同时更新范围的左右端
			swap(a[i],a[j]);
			i++;
			j--;
		}
	}
}

int main()
{
	int a[10] = {7,5,9,2,6,4,3,8,1};
	Move(a,9);
	for(int i=0;i<9;i++)
	{
		cout<<a[i]<<" ";
	}
	return 0;
}

三:试着重新编写 快速排序的划分算法,使之每次选取的枢轴值都是随机地从当前子表中选择的

核心代码 

void Partiton2(int a[],int low,int high,)
{
	//获取随机下标
	int root=low+rand()%(high-low+1);
	//交换到第一个
	swap(a[low],a[root]);
	//i一开始指向low位置
	int i = low;
	//从第二个开始遍历到结束
	int p = a[low];
	for(int j=low+1;j<=high;j++)
	{
		//如果小于基数值
		if(a[j]<p)
			swap(a[++i],a[j]);
		//交换到前面去
	}
	//将基准元素插入到最终的位置
	swap(a[low],a[i]);
	//返回基准元素的位置
	return i;
}

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

#include<iostream>
using namespace std;

int min_k(int a[],int low,int high,int k)
{
	//快速排序划分算法
	//i一开始指向low位置
	int i=low;
	//从第二个开始遍历到结束
	int p=a[low];
	for(int j=low+1;j<=high;j++)
	{
		//如果小于基准值
		if(a[j]<p)
			swap(a[++i],a[j]);
		//交换到前面去
	}
	//将基准元素插入到最终的位置
	swap(a[low],a[i]);
	//返回到基准元素的位置
	if(i==k) return a[i];
	//若最后基准的位置为k的话 那么基准就是第k小
	else if(i>k) return min_k(a,low,i-1,k);
	//若最后基准的位置>k的话 那么基准左边递归去找
	else return min_k(a,i+1,high,k);
	//若最后基准的位置<k的话 那么基准右边递归去找
}
int main()
{
	int a[10] = {0,7,5,9,2,6,4,3,8,1};
	cout<<min_k(a,1,9,8)<<endl;
	return 0;
}

五:荷兰国旗问题:设有一个仅由红,白,蓝三种颜色的条块组成的条块序列,请编写算法,使得这些条块按红 白 蓝的顺序排好,即排成荷兰国旗图案

#include<iostream>
using namespace std;

//按照负数0 正数来排数组
void num(int a[],int n)
{
	//三个指针的指向 初始化
	int i=0,j=0,k=n-1;
	//循环体 j<=k
	while(j<=k)
	{
		int v;
		if(a[j]>0) v=1;
		else if(a[j]==0) v=0;
		else v=-1;
		//switch判断a[j]的值
		switch(v)
		{
			//负数 和i交换
		case -1:
			swap(a[i],a[j]);
			i++;
			j++;
			break;
			//0 遍历指针+1
		case 0:
			j++;
			break;
			//正数 和k交换
		case 1:
			swap(a[j],a[k]);
			k--;
			break;
		}
	}
}

int main()
{
	int a[9] = {0,-1,-2,0,0,3,-4,1};
	num(a,8);
	for(int i=0;i<8;i++)
	{
		cout<<a[i]<<"   ";
	}
	return 0;
}

六:已知由n(n>=2)个正整数构成的集合A={ak|0<=k<n},将其划分成为两个不相交的子集A1和A2。元素个数分别是n1和n2,A1和A2中的元素之和分别为S1和S2,设计一个算法,满足|n1-n2|最小且|S1-S2|最大

#include<iostream>
using namespace std;

//快排
void seta(int a[],int n,int low,int high)
{
	//非递归
	int flag=1;
	//取中间值 后面与基准值进行比较
	int k=(high-low)/2;
	//非递归条件
	while(flag)
	{
		//快排
		//i一开始指向low位置
		int i=low;
		//从第二个开始遍历到结束
		int p=a[low];
		for(int j=low+1;j<=high;j++)
		{
			//如果小于基准值
			if(a[j]<p)
				//交换到前面去
				swap(a[++i],a[j]);
		}
		//将基准元素插入到最终的位置
		swap(a[low],a[i]);
		//返回基准元素的位置

		//如果基准值刚好等于s1的右端 结束
		if(i==k-1) flag=0;
		//如果基准值小于 则在右端进行快排 继续找到中间值的位置
		else if(i<k-1) low=i+1;
		//否则在左端
		else high = i-1;
	}
	//计算两个集合的和
	int s1=0,s2=0;

	for(int i=0;i<k;i++)
	{
		s1+=a[i];
		cout<<"s1="<<a[i]<<" ";
	}
	cout<<endl;

	for(i=k;i<n;i++)
	{
		s2+=a[i];
		cout<<"s2="<<a[i]<<" ";
	}
	cout<<endl;
	cout<<abs(s1-s2)<<endl;
}

int main()
{
	int a[9]={0,-1,-2,0,-10,3,-4,1};
	seta(a,8,0,8);
	return 0;
}

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

#include<iostream>
using namespace std;

//链表存储结构
typedef struct lnode{
  int data;
  struct lnode *next;
}lnode,*linklist;

int a[10]={7,5,9,2,6,4,3,8,1};
int n=9;

void buildlist(lnode *L)
{
    lnode *s,*r=L;
    r->data=a[0];
    if(n==1) r->next=NULL;
    else{
        for(int i=1;i<n;i++)
        {
            s=(lnode *)malloc(sizeof(lnode));
            s->data=a[i];
            r->next=s;
            r=r->next;
        }
        r->next=NULL;
    }
}

void disp(lnode *L)
{
    lnode *s=L;
    while(s)
    {
        cout<<(s->data)<<" ";
        s=s->next;
    }
    cout<<endl;
}

//简单选择排序
void selectsort(linklist &l)
{
	//五个指针 遍历表的指针的初始化
	lnode *h = l;
	lnode *p,*s,*r,*q;
	//将表置空
	l= NULL;
	//最外面的循环条件
	while(h!=NULL)
	{
		//最大节点和工作指针的初始化
		s=p=h;
		//前驱节点的初始化
		q=r=NULL;
		//每一趟开始遍历
		while(p!=NULL)
		{
			//碰到更大的节点
			if(p->data>s->data)
			{
				//更新最大节点的位置
				s=p;
				//更新最大节点的前驱
				r=q;
			}
			q=p;
			p = p->next;
			//工作指针向后遍历时前驱指针和工作指针向后移
		}
		//若最大的是表头 将遍历表的指针后移
		if(s==h) h = h->next;
		else
		{
			r->next = s->next;
		}
		//插入结果链表当中
		s->next = l;
		//更新结果链表的头
		l=s;
	}
}

int main()
{
	lnode L;
	lnode *l = &L;
	buildlist(l);
	selectsort(l);
	disp(l);
	return 0;
}

八:试着设计一个算法,判断一个数据序列是否构成一个小根堆

#include<iostream>
using namespace std;

//判断是不是小根堆
int is_min(int a[],int len)
{
	//最后的序号为偶数
	if(len%2==0)
	{
		//判断单只节点
		if(a[len/2]>a[len]) return 0;
		//判断双支节点
		for(int i=len/2;i>=1;i--)
		{
			if(a[i]>a[2*i]||a[i]>a[2*i-1])
				return 0;
		}
	}
	else
	{
		//为奇数
		for(int i=len/2;i>=1;i--)
		{
			//判断双支节点
			if(a[i]>a[i*2]||a[i]>a[i*2+1])
				return 0;
		}
	}
	return 1;
}

int main()
{
	int a[8]={1,1,3,2,5,4,0};
	cout<<is_min(a,6)<<endl;
	return 0;
}

九:设顺序表用数组A[ ]表示,表中元素存储在数组下标1 -  m+n范围内,前m个元素递增有序,后n个元素递增有序,设计一个算法,使得整个顺序表有序

#include<iostream>
using namespace std;

void insertsort(int 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];
		//插入哨兵值
	}
}

int main()
{
	int a[8]={0,1,3,5,7,2,4,9};
	insertsort(a,4,3);
	for(int i=1;i<=7;i++) cout<<a[i]<<" ";
	return 0;
}

十:有一种简单的排序算法,称为计数排序,这种排序算法对一个待排序的表(用数组表示)进行排序,并将排序结果存放到另一个新的表中,必须注意的是,表中所有待排序的关键码互不相同,计数排序算法针对表中的每个记录,扫描待排序的表一趟,统计表中有多少个记录的关键码比该记录的关键码小,假设针对某个记录统计出的计数值为c,则这个记录在新有序表中的合适存放位置为c

#include<iostream>
using namespace std;

void countsort(int a[],int b[],int n)
{
	//遍历每一趟
	int count=0;
	for(int i=0;i<n;i++)
	{
		//找到每个元素小的个数
		count = 0;
		for(int j=0;j<n;j++)
		{
			if(a[j]<a[i]) count++;
		}
		b[count] = a[i];
	}
}
int main()
{
	int a[6]={3,5,7,2,4,9},b[6];
	countsort(a,b,6);
	for(int i=0;i<6;i++) cout<<b[i]<<" ";
	return 0;
}

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

#include<iostream>
using namespace std;

int quicksort(int a[],int low,int high)
{
	//i一开始指向high位置
	int i=high;
	//从倒数第二个开始遍历到头
	int p=a[high];
	for(int j=high-1;j>=0;j--)
	{
		//如果大于基准值
		if(a[j]>p) swap(a[--i],a[j]);
		//交换到后面去
	}
	//将基准元素插入到最终的位置
	swap(a[high],a[i]);
	//返回基准元素的位置
	return i;
}

int main()
{
	int a[5]={3,5,7,2,4};
	cout<<quicksort(a,0,4)<<endl;
	return 0;
}

猜你喜欢

转载自blog.csdn.net/m0_56051805/article/details/125945515