各种排序算法的总结和实现。

最近在准备找工作,听说基本的排序算法是必考的,尤其是堆排序和快速排序。所以想自己实现一下,有些算法实现的时候,有困难,自己没能成功的实现。后来上网查到了一些前辈的博客。很不幸的是有些前辈的的博客中的排序算法是错误的。而有些虽然正确,但是算法的实现的非常的晦涩,很难看懂。所以自己总结了一下,挑选了一下,实现了一下。本页所有的排序算法经过测试能够正确运行。


现保留于此,以后有时间再来看看。 


#include "stdafx.h"
#include "C:\WORK\main.h"
#include "string.h"
//------------------------------------------------------------------------------------------
//起泡排序
void  print(int * array, int length)
{
	for (int i = 0; i < length;i++)
	{
		cout << array[i] << " ";
	}
	cout <<"\n"<<endl;
}

void print_line(void)
{
	cout << "------------------------------------------" << endl;
}

void swap(int & a, int & b)
{
	int temp = a;
	a = b;
	b = temp;
}


void bubbleSort(int * array, int n)
{
	for (int i = 0; i < n - 1;i++)//因为数组下标是从0开始到n-1
	{
		for (int j = 0; j < n - 1 - i;j++)//每次经过排序之后都有一个元素进入最终位置,被放置到数组的最后一个元素的位置。所以只要调整剩下的0 -- n-1-i个元素就行
		{
			if (array[j]>array[j+1])
			{
				swap(array[j], array[j + 1]);
			}
		}
	}
}

void bubbleSort_example()
{
	print_line();
	cout << "BubbleSort:\n" << endl;
	int array[] = { 7, 9, 1, 4, 8, 5, 6, 3 };
	int len = sizeof(array) / sizeof(array[0]);
	print(array, len);
	bubbleSort(array, len);
	print(array, len);
	print_line();
}
//------------------------------------------------------------------------------------------


//------------------------------------------------------------------------------------------
//快速排序
int  partition(int * array, int lower, int higher)//这是分割算法
{
	int i = lower;
	int j = higher;
	while (i<j)//j永远是--,i用于是++
	{
		while (array[j]>array[i])
		{
			j--;
		}
		if (i<j)
		{
			swap(array[i], array[j]);
			i++;
		}
		while (array[i]<array[j])
		{
			i++;
		}
		if (i<j)
		{
			swap(array[i], array[j]);
			j--;
		}
  }
	return i;
}

void  quicSort(int * array, int lower, int higher)
{
	if (lower<higher)
	{
		int mid = partition(array, lower, higher);
		quicSort(array, lower, mid - 1);
		quicSort(array, mid+1, higher);
	}
}

void quicSort_example()
{
	print_line();
	cout << "quickSort:\n" << endl;
	int array[] = { 7, 9, 1, 4, 8, 5, 6, 3 };
	int len = sizeof(array) / sizeof(array[0]);
	print(array, len);
	quicSort(array,0,len-1);
	print(array, len);
	print_line();
}
//------------------------------------------------------------------------------------------
//------------------------------------------------------------------------------------------
//选择排序

void selectsort(int * array, int len)
{
	int n = len;
	int min;//用来保存每次选出的最小元素的下标
	for (int i = 0; i < n - 1; i++)//因为数组下标是从0到n-1
	{
		min = i;
		for (int j = i + 1; j < n; j++)//每次都有一个最小的元素被放置到应该放置的位置,所以每次j要从当前的i的下一个位置开始
		{
			if (array[j] < array[min])
			{
				min = j;//如果min不是当前的这次排序中的最小的元素,那么就把这个次选中的最小元素的下标j赋值给min
			}
		}
		if (i != min)//如果现在的下标和刚开始进入循环时候选中的那个最小元素的下标不相等,那么就交换两个元素的位置,让最小的元素到前面去
		{
			swap(array[i], array[min]);
		}
	}
}

void selectsort_example()
{
	print_line();
	cout << "selectsort:\n" << endl;
	int array[] = { 7, 9, 1, 4, 8, 5, 6, 3 };
	int len = sizeof(array) / sizeof(array[0]);
	print(array, len);
	selectsort(array,len);
	print(array, len);
	print_line();
}
//------------------------------------------------------------------------------------------
//------------------------------------------------------------------------------------------
//插入排序
void   insertsort(int * array, int   n)
{
	for (int i = 1; i < n; i++)
		//因为认为第一个元素array[0]已经是有序的,所以从array[1]开始进行向后的插入
	{
		int   tmp = array[i];
		int   j = i - 1;
		while (j >= 0 && tmp < array[j])
		{
			array[j + 1] = array[j];
			//移动前面的已经排好序的元素,找到当前元素的正确的插入位置
			j--;
		}
		array[j + 1] = tmp;//为什么要j+1,因为j可能在第一个元素的前面,也就是j=-1;
	//因为上面的循环中的条件是j>=0之后就可能要进行j--操作。所以j==0时候j--那么j=-1表示第一个元素前面的那个空位置。
	}
}


void insertsort_example()
{
	print_line();
	cout << "insertsort:\n" << endl;
	int array[] = { 7, 9, 1, 4, 8, 5, 6, 3 };
	int len = sizeof(array) / sizeof(array[0]);
	print(array, len);
	insertsort(array, len);
	print(array, len);
	print_line();
}
//------------------------------------------------------------------------------------------
//------------------------------------------------------------------------------------------
//希尔排序
void shellSort(int * array, int length)
{//因为是三重循环所以需要三个变量。所以我们这里定义了i,j,k三个变量。
	int i, j, d;//这里的d表示的是增量,最开始的时候d=length/2;然后每次递归除以2。
	int temp;//用来在插入排序的时候进行变量的保存
	for (d = length / 2; d > 0;d=d/2)//第一层循环的作用很简单,就是用来每次把增量缩小一半。
	{
		for (i = d; i < length;i++)//第二个循环的作用就是从增量位置开始向后移动
		{
			temp = array[i];
			for (j = i - d; j >= 0; j = j - d)//第三个循环的作用是对每个交换对内部进行插入排序
			{
				if (temp < array[j])
				{
					array[j + d] = array[j];
				}
				else
				{
					break;
				}
			}//endfor
			array[j + d] = temp;//注意j的值是在前面经过j=i-d;计算出来的。
		}//endfor
	}//endfor	
}

void shellsort_example()
{
	print_line();
	cout << "shellsort:\n" << endl;
	int array[] = { 7, 9, 1, 4, 8, 5, 6, 3 };
	int len = sizeof(array) / sizeof(array[0]);
	print(array, len);
	shellSort(array, len);
	print(array, len);
	print_line();
}
//------------------------------------------------------------------------------------------
//------------------------------------------------------------------------------------------
/*
									7
								/         \
							  9            16
							 /  \        /     \ 
						   4      8    5        6
						  /
						 3

注意【 0     1       2     3       4     5     6     7 】一共8个元素
       7     9       16    4       8     5     6     3
为什么是(n-2)/2是最后一个分支节点?因为我们可以找出节点位置的对应关系,然后返回来推到公式。
堆中元素个数      最后一个分支节点的下标位置
		n=2               0
		n=3               0
		n=4               1
		n=5               1
		n=6               2
		n=7               2
		n=8               3
所以我们可以推出:最后一个分支节点的位置是:(n-2)/2
*/
//堆排序
//调整节点 大根堆
template<class T>
void AdjustHeapNode(T * array, int i, int n) //(1)调整节点i,数组共有N个节点
{

	if (n == 1 || i>(n - 2) / 2)//如果i是叶子节点,那么不做任何处理//
		/*
		i为叶子节点,n==1表示只有一个结点,而i>(n-2)/2表示的是i一个叶子节点,没有左右孩子 
		因为(n-2)/2 最后一个非叶子节点(分支节点)的位置,画出一个堆的完全二叉树就可以看到 。
		而且一定要注意(n-2)/2是最后一个非叶子节点的位置,而不是(n-1)/2。如果写成(n-1)/2那么排序结果错误。

		因为建堆的过程,实际就是调整整个完全二叉树中的所有非叶子节点的过程。所以我们从最后一个非叶子节点开始。
		就像数据结构笔记上面的调整方法一样是,堆的操作是:插入是向上调整,删除是向下调整。
		*/
		return;

	int iLeft = 2 * i + 1;//节点i的左孩子的位置例如:如果完全二叉树的根是i=0;那么左孩子是1 
	int iRight = 2 * i + 2;//节点i的右孩子的位置例如:如果完全二叉树的根是i=0;那么右孩子是2 


	if (iRight <= n - 1)     //(2)说明i有左右两个子节点         三个节点找最大值//
		/*比如2号位置的节点元素16,它的右孩子元素是6而且它的位置也是6,因为6<n-1=8-1=7所以我们可以知道2号位置的元素
		16有左孩子和右孩子。所以我们要在这个节点本身和它的左孩子,右孩子,中找到一个 最大的节点。*/
	{
		if (array[i] >= array[iLeft] && array[i] >= array[iRight])      // i 最大 不用调整
			return;

		if (array[i]<array[iLeft] && array[iRight] <= array[iLeft])  // iLeft 最大,左孩子大于根节点,大于右孩子节点 
		{
			swap(array[iLeft], array[i]);
			AdjustHeapNode(array, iLeft, n);//然后递归调整左孩子节点 
			return;
		}

		if (array[i]<array[iRight] && array[iLeft] <= array[iRight]) // iRight 最大,右孩子大于根节点,大于左孩子节点 
		{
			swap(array[iRight], array[i]);
			AdjustHeapNode(array, iRight, n);//然后递归调整右孩子节点 
			return;
		}

	}
	else{ // (3)说明i只有左节点   二个节点找最大值  //  

		//iLeft为最后一个节点
		/*比如在图中我们的3号位置的元素4的左孩子是7号位置的3,而我们的3号位置的4的右孩子是8号位置上的空元素*/

		if (array[i] >= array[iLeft])
			return;
		else
		{
			swap(array[iLeft], array[i]);
			AdjustHeapNode(array, iLeft, n);//然后递归的调整左孩子 
			return;
		}

	}
}


//建立堆
template<class T>
void CreateHeap(T * array, int n)
{

	int iFirst = (n - 1) / 2; //(4)建堆的时候从最后一个具有分支的结点开始调整//
	/*在这里n=8;n-1=7;7/2=3.5;所以(int) 3.5=3所以3是最后一个非叶子节点的位置 
	接下来使用循环一次调整每一个非叶子节点的位置,使整棵完全二叉树成为一个堆。 */
	for (; iFirst >= 0; iFirst--)
	{
		AdjustHeapNode(array, iFirst, n);
	}
}

//堆排序
template<class T>
void HeapSort(T * array, int n)
{
	CreateHeap(array, n);//创建一个堆 
	for (int i = 0; i < n - 1; i++)
	{
		swap(array[n - 1 - i], array[0]);
		/*调整元素位置,比如第一次,堆中的最大的元素始终在a[0] 的位置,那么我就叫唤a[0] 和堆中的最后一个元素的位置。
		交换a[0]和a[n-1],然后在依次交换a[0]和a[n-2],a[0]和a[n-3]等等。交换完成之后,剩下的元素就不能构成堆了
		所以我们要重新调整剩下的元素构成的数组使它能够重新的形成一个堆,然后继续这个过程。*/
		AdjustHeapNode(array, 0, n - 1 - i);
	}
}

void heapsort_example()
{
	print_line();
	cout << "heapsort:\n" << endl;
	int array[] = { 7, 9, 1, 4, 8, 5, 6, 3 };
	int len = sizeof(array) / sizeof(array[0]);
	print(array, len);
	HeapSort(array, len);
	print(array, len);
	print_line();
}
//------------------------------------------------------------------------------------------
//------------------------------------------------------------------------------------------
//二路归并排序
/*
把source中的len长度的数据,复制到dest中的从first开始的位置。
source:源数组
dest:目标数组
len:源数组长度
first:目标数组起始位置
*/
void copyArray(int  * source, int * dest, int len, int first)
{
	int j = first;
	for (int i = 0; i < len; i++)
	{
		dest[j] = source[i];
		j++;
	}
}
/*
array表示要合并的数组
left表示起始位置
right表示终止位置
*/
void merge(int * array, int left, int right)
{
	int begin1 = left;
	int mid = (left + right) / 2;
	int begin2 = mid + 1;
	int k = 0;//这个k用来表示数组b的下标。
	int newArrayLen = right - left + 1;//数组下标是0-n1,所以数组长度是(n-1)-0+1=n
	int *b = (int*)malloc(newArrayLen*sizeof(int));
	while (begin1 <= mid && begin2 <= right)//如果前半段数组和后半段数组都有元素,那么就执行这个循环
	{
		if (array[begin1] <= array[begin2])
			b[k++] = array[begin1++];//把较小的元素放入到b数组中
		else//array[begin1] > array[begin2]
			b[k++] = array[begin2++];//把较小的元素放入到b数组中
	}
	while (begin1 <= mid)//如果后半段数组中没有元素了,那么就把前半段的所有元素拷贝到b数组中
		b[k++] = array[begin1++];
	while (begin2 <= right)//如果前半段数组中没有元素了,那么就把后半段的所有元素拷贝到b数组中
		b[k++] = array[begin2++];
	copyArray(b, array, newArrayLen, left);//把b数组中的元素拷贝到array数组中去,从left开始
	free(b);
}

void mergeSort(int * array, int left, int right)
{
	int mid;
	// 保证至少有两个元素
	if (left < right)
	{
		mid = (left + right) / 2;
		mergeSort(array, left, mid);//把前半段数组进行递归的归并排序
		mergeSort(array, mid + 1, right);//把后半段数组进行递归的归并排序
		merge(array, left, right);//把排好序的前半段数组和后半段数组进行合并。
	}
}

void mergesort_example()
{
	print_line();
	cout << "mergesort:\n" << endl;
	int array[] = { 7, 9, 1, 4, 8, 5, 6, 3 };
	int len = sizeof(array) / sizeof(array[0]);
	print(array, len);
	mergeSort(array,0, len-1);
	print(array, len);
	print_line();
}


int main()
{
	bubbleSort_example(); //起泡排序
	quicSort_example();//快速排序
	selectsort_example();//选择排序
	insertsort_example();//插入排序
	shellsort_example();//希尔排序
	heapsort_example();//堆排序
	mergesort_example();//归并排序
	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值