学习日记——排序算法C++实现

须知

算法使用模板进行编写,有1个错误的排查并没有完成,我会标注在代码上的

递归——顺序查找算法

因为代码十分简短,故没有对代码逻辑进行描述,递归就行了

template<class T>
int sequential_search(T a[], int n,const T&x)
{
	if (n < 1)return -1;
	if (a[n - 1] == x)return n - 1;
	else return sequential_search(a, n - 1, x);
}

等级排序

这个算法的思想就是a[]为输入的乱序数组,n为元素数量,r[]用来装载数组a中每个元素在数组中的大小顺序,但没有对a进行排序移位,下边还有一段被注销的代码,那个算法的效率是比上边算法效率低的,然后下边的rear_range函数就是用来对数组a依据数组r进行重排序的,n依然为数组大小

//template<class T>
//a[]原本是T类的声明
void rank_(int a[], int n, int r[])
{
	for (int i = 0; i < n; i++)
	{
		r[i] = 0;
	}//初始化
	for (int i = 1; i < n; i++)
	{
		for (int j = 0; j < i; j++)
		{
		if (a[j] <= a[i])
			r[i]++;
		else 
			r[j]++;
		}
	}
	//j从0开始,为内循环,当j=i时停止内循环
	//i从1开始,每一个a[i]都和它前边的i个数比较,大的顺序自增,如a中第二个三,达成一次自增,则顺序为1
	//如果出现后边还有小的数,如4,3,9,3,7,2
	//第一个3和前边的4对比,4的序号加一,轮到2和4,3,9,3,7对比时,它们全增一
	//其实这就是一个排序
	//不同于书上的排序
	/*
	for (int i = 0; i < n; i++)
	{
		for (int j = 0; j < n; j++)
		{
			if (a[j] >= a[i])
				r[j]++;
			else
				r[i]++;
		}
	}
	//此处j也进行n次循环,则r中最小的数会为2,而且是两个2
	//每一个a[i]都和所有的a对比,会出现和自己对比
	//自增一次,和另一个相同的数对比,自增一次,结果即2
	//故当数组内没有相同的数时最小的排序值为1
	//出现两个相同的数时则为2,以后同理,用j<i作为条件可以避免
	//每一个a[i]只和它前边的对比,不会和自身对比故从零自增
	//后边相同的数和自己对比出现if中的条件
	//然后增的是后来的,不是内循环中的,故还能排序先出和后出现
	*/
template<class T>
void rear_range(T a[], int n, int r[])
{
	T*u[] = new T[n];
	for (int i = 0; i < n; i++)
		u[r[i]] = a[i];
		//把a中的数按照r得到的名次进行排序
		//即4去3号位,3去1号位,9去5号位,3去2号位,7去4号位
	for (int i = 0; i < n; i++)
		a[i] = u[i];
	delete[]u;
}
}

最大值函数和交换函数

实际上进行排序的时候,加入一些简单的工具性函数更高效(写代码的时候)

int index_of_max(T a[], int n)
{//找最大值函数,n为数组前n个数
	if (n <= 0)
	{
		cout << "error!" << endl;
		return - 1;
	}
	//从前n-1个数中找到最大的数,进行n-1次比较
	int indexofmax = 0;
	for (int i = 1; i < n; i++)
		if (a[indexofmax] < a[i])
			indexofmax = i;
	return indexofmax;
}
void swap(int&a,int&b)
{
	int	temp = a;
	a = b;
	b = temp;
}

选择排序算法1

这个算法比较简单暴力,很容易懂,找到最大的,放到最后,然后不管了,开始循环这样的操作

template<class T>
void selection_sort(T a[], int n)
{//选择排序
	for (int i = n - 1; i > 0; i--)
	{
		int j = index_of_max(a, i);
		//得到前i个数中最大的值的位置,下一次i自减
		swap(a[i], a[j]);
		//交换第i+1和最大值数的位置
		//此选择排序执行次数每次都不会变,即总为n-1次
	}
}

选择排序算法2

这个算法设置了flag,可以用来减少排序次数,并且每次排序的时候会检查整个数组是否完成排序,可以算是优化了吧

template<class T>
void selectionSort(T a[], int n)
{
	bool sorted = false;
	for (int size = n; !sorted && (size > 0); size--)
	{//从后往前放,从大放到小
		int index_of_max = 0;
		//不管最大的一开始是哪个,先从第一个开始
		sorted = true;
		//如果没有出现逆序,就停止最外层循环
		for (int i = 0; i < size; i++)
		{//不管已经排序的部分,减少排序次数
			if (a[index_of_max] < a[i])index_of_max = i;
			//若出现当前index错误则纠正
			else sorted = false;
			//若内循环的全部次数里出现了一次index错误即表示没有排序完,需要再进行排序
		}
		swap(a[index_of_max], a[size - 1]);
		//对当前未排序的部分的最后一个数进行排序
	}
}

冒泡算法

相邻比较,两个相邻的数大的向右移动,小的向左移动,就算冒个泡,冒泡次数很明显是不会变化的

//template<class T>
void bubble(int	a[], int n)
{//把a[0:n-1]中最大元素移到最右边
	for (int i = 0; i < n - 1; i++)
		if (a[i] > a[i + 1])
			swap(a[i], a[i + 1]);
}
//template<class T>
void bubble_sort(int a[], int n)
{
	for (int i = n; i > 1; i--)
		bubble(a, i);
}

及时终止的冒泡算法

变化在哪?冒泡算法用了bool型,设立了flag,如果一次冒泡中后边的数存在已完成排序的情况,由flag表示冒泡完成,减少的排序次数应该是在冒泡排序算法上,如果某一次冒泡时已经是顺序了,就不用冒泡

template<class T>
bool bubble(T a[], int n)
{
//及时终止的冒泡
	bool swapped = false;//先设置一个flag为false
	for (int i = 0; i <= n - 1; i++)
	{
	//循环n次
		if (a[i] > a[i + 1])
		{
			swap(a[i], a[i + 1]);//传统冒泡行为
			swapped = true;//如果存在冒泡行为即表示没有完成排序
		}
	}
	return swapped;
}
template<class T>
void bubble_sort(T a[], int n)
{
	for (int i = n - 1; i > 0 && bubble(a, i); i--);
	//最多进行n次循环,如果存在没有冒泡行为就停止
	//即当bubble为false,即完成排序,即可省略之后的循环次数
}

插入排序1

插入排序总共有三个算法,这是第一个

template<class T>
void insert0(T a[], int& n, const T&x)
{
//插入排序即在有序数组中插入一个元素
//其中n为数组中元素个数,并不是数组最大容量
//数组最后一个数为a[n-1]
	int i;//i需申明在循环以外,否则会报错的
	for (i = n - 1; i >= 0 && x < a[i]; i--)
		a[i + 1] = a[i];//比x大的数往后移
	a[i + 1] = x;
	//跳出循环即x>=a[i],则有x应当处于a[i]后边一位
	n++;//总数加一
}

插入排序2

插入排序总共有三个算法,这是第二个

template<class T>
void insert(T a[], int& n, const T&x)
{//插入排序即在有序数组中插入一个元素
	//其中n作为最后一个数的标志,即把x插入有序数组a[0:n-1]中
	//下边用循环中变化的i表示有序数组的大小
	//其中n为数组中元素个数,并不是数组最大容量
	//数组最后一个数为a[n-1]
	int i;//i需申明在循环以外
	for (i = n - 1; i >= 0 && x < a[i]; i--)
		a[i + 1] = a[i];//比x大的数往后移
	a[i + 1] = x;
	//跳出循环即x>=a[i]
	//则有x应当处于a[i]后边一位
}
template<class T>
void insert_sort(T a[], int n)
{
	for (int i = 1; i < n; i++)
	{
		T t = a[i];
		//获得当前最后一个未排序的数值
		insert(a, i, t);
		//就像放麻将一样,拿起最后未排序的数放入应该放的位置
		//所有比它大的数提前后移,这是插入与放麻将的一点区别
	}
}

插入排序3

插入排序总共有三个算法,这是第三个

template<class T>
void insertionsort(T a[],int n)
{
	for (int i = 1; i < n; i++)
	//内循环决定从后向前进行对比,则外循环需从零开始增加
	{
		T t = a[i];//当前未排序的数的值
		int j;
		for (j = i-1; j >=0 && (t < a[j]);j--)
		//①前面一个数跟后面一个数对比,从后向前比
		//只有当前未排序的数小于以此遇到的数才能继续循环
		a[j+1]=a[j];
		//②因为已经用t寄存当前插入数值,故可视后边一个位置为空
		//各个比当前数值大的数都后移,以留出当前数值的位置
		a[j + 1] = t;
		//③此时j记录的是比当前数值小的数的位置
		//因为当前数值要放在该位置之后一位,故用j+1
	}
}

归并排序(正确)

这是网上借鉴来的一个算法,我用三目运算符简化了一下代码

//对已排好的两个数组进行合并
void merge(int *arr, int low, int mid, int high)
{//从数组arr[low:high]
	int i, j, count = 0;
	int *data = new int[high - low + 1];
	for (i = low, j = mid + 1; i <= mid && j <= high; count++)data[count] = (arr[i] < arr[j]) ? arr[i++] : arr[j++];
	//进行循环直到for循环里的条件被破坏,则一定会在下列两种情况中选出一种
	for (; i <= mid;count++,i++)data[count] = arr[i];
	for (; j <= high;count++,j++)data[count] = arr[j];
	i = low;
	for (int k = 0; k < count&&i <= high; k++, i++)arr[i] = data[k];
	delete[] data;
}
//分而治之,将长数组一直进行二等分,当分到只剩一个时返回
void Msort(int* arr, int low, int high)
{
	if (low >= high){return;}
	int mid = (low + high) / 2;
	Msort(arr, low, mid);
	Msort(arr, mid + 1, high);
	merge(arr, low, mid, high);
}

归并排序(故障)

这个算法来自《无处不在的算法》机械工业出版社,原本好像是java写的,我用c++改了一下,但改来改去都不能出正确结果,有大佬会改的话求指教!我是真的尽力从理论上在修改过了一个晚上,但是奈何…其实放在这里既算是一个记录,也是一个求助

template<class T>
void merge(T a[], int al, int ar, T b[], int bl, int br, T c[])
{
//用三目运算符在ar和br之前把小的数放入c中,遇到超出ar或br就放没超出的那个,执行次数为两个数组之和
//该函数用于把两个有序的数组合并到一起
	int i = al, j = bl;
	for (int k = 0; k < ar - al + br - bl + 1; k++)
	{
		if (i < ar&&j < br)c[k] = (a[i] < b[j]) ? a[i++] : b[j++];
		if (i > ar)c[k] = b[j++];
		if (j > br)c[k] = a[i++];
	}
}
template<class T>
void merge_sort(T a[], int al, int ar)
{
	if (ar > al)
	{
		int m = (ar + al) / 2;//找到中间点
		merge_sort(a, al, m);
		//用递推法把a在m的左右逐步分为最小个数,然后执行下方排序
		merge_sort(a, m + 1, ar);
		T*b = new T[ar - al + 1];
		//声明一个新数组临时存储有序的a
		merge(a, al, m, a, m + 1, ar, b);
		//把有序的数逐步合起来
		for (int i = 0; i < ar - al + 1; i++)
			a[al + i] = b[i];
	}
}

快速排序

代码里含有用于观察数字如何变化的cout代码,都已经注释掉了,可以直接用哒

void quickSort(int *A, int al, int ar)
{
	if (al < ar)
	{
		int pivot = A[al];
		int i = al, j = ar + 1;
		while (true)
		{
			while (A[++i] > pivot&&i < ar) 
			{ 
				//cout << "达成 i 的移动" << endl; 
			};
			while (A[--j] < pivot&&j > al) 
			{ 
				//cout << "达成 j 的移动" << endl; 
			};
			if (i < j)
			{
				//cout << "交换" << A[i] << " 和 " << A[j] << endl;
				swap(A[i], A[j]);
			}
			else
				break;
		}
		//cout << endl << "还未移动pivot,此时已完成除pivot以外的所有数的移动,当前数组顺序如下:" << endl;
		//for (int k = 0; k < ar - al + 1; k++)
			//cout << A[k] << " ";
		//cout << endl;
		//cout << " j= " << j <<" i= "<<i<< endl;
		//上方步骤用来把pivot两边的数值堆起来,两边的里层顺序并没有排序
		swap(A[al], A[j]);
		//此步骤用于把pivot放到它应该在的位置
		//之后就是把pivot的左右两边用递归来排序的,也是再找各自的左边第一个数为pivot,然后把pivot再放到正确位置,再对这个pivot的左右进行排序,然后回归到最外层
		quickSort(A, al, j - 1);
		quickSort(A, j + 1, ar);
	}
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值