各类算法实现(sort)

#ifndef __INSERTSORT_H__
#define __INSERTSORT_H__
#include "QuickSort.h"
#include <stdio.h>


/*
#######################################################################################
插入排序(Insertion Sort)的算法描述是一种简单直观的排序算法。
它的工作原理是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,
找到相应位置并插入。插入排序在实现上,通常采用in-place排序(即只需用到O(1)的额外空间的排序)
因而在从后向前扫描过程中,需要反复把已排序元素逐步向后挪位,为最新元素提供插入空间。

步骤:

从第一个元素开始,该元素可以认为已经被排序
取出下一个元素,在已经排序的元素序列中从后向前扫描
如果该元素(已排序)大于新元素,将该元素移到下一位置
重复步骤3,直到找到已排序的元素小于或者等于新元素的位置
将新元素插入到该位置中
重复步骤2
########################################################################################
*/

//将数组分为两侧,一侧有序,另一侧无序,每次从无序数组中选取一个数字,插入到有序数组的相应位置,使其有序
void InsertSort(int arr[], int left, int right)//直接插入排序,拿到这个数,
                                            //与前面已排好的数比较,直到temp大于已排好的一个数在成块后移,插入
{
	int temp;
	int j = 0;//后一个与前一个已排好的序列比较
	for (int i = 1; i <= right; i++)
	{
		temp = arr[i];
		for (j = i; j >=0 && arr[j - 1] > temp; j--)
			arr[j] = arr[j - 1];//成块后移
		arr[j] = temp;//把temp排好
	}
}

/*
#########################################################
希尔排序,也称递减增量排序算法,是插入排序的一种高速而稳定的改进版本。

希尔排序是基于插入排序的以下两点性质而提出改进方法的:

1、插入排序在对几乎已经排好序的数据操作时, 效率高, 即可以达到线性排序的效率

2、但插入排序一般来说是低效的, 因为插入排序每次只能将数据移动一位>
#########################################################
*/

//void ShallinsertSort(int arr[],int right)//希尔排序
{
 int len = right+1;
 if(len<=1||arr==NULL)
  return;
 for(int div=len/2;div>=1;div=div/2)//定增量div,并不断减小
 {
  for(int i=0;i<=div;++i)//分组成div组
  {
   for(int j=i;j<len-div;j+=div)//对每组进行插入排序
    for(int k=j;k<len;k+=div)
     if(arr[j]>arr[k])
      Swap(arr[j],arr[k]);//交换两个数的值
  }
 }
}
#endif
Shell排序的时间性能优于直接插入排序
希尔排序的时间性能优于直接插入排序的原因:
①当文件初态基本有序时直接插入排序所需的比较和移动次数均较少。
②当n值较小时,n和  的差别也较小,即直接插入排序的最好时间复杂度O(n)和最坏时间复杂度0(  )差别不大。
③在希尔排序开始时增量较大,分组较多,每组的记录数目少,故各组内直接插入较快,后来增量di逐渐缩小,分组数逐渐减少,而各组的记录数目逐渐增多,但由于已经按di-1作为距离排过序,使文件较接近于有序状态,所以新的一趟排序过程也较快。
因此,希尔排序在效率上较直接插入排序有较大的改进。
//#######################################################
//冒泡排序
#ifndef __BUBBLESORT_H__
#define __BUBBLESORT_H__
#include "QuickSort.h"
#include <stdio.h>
//不停地交换比较arr[i]与arr[i+1]   使最大值下沉
void BubbleSort1(int arr[], int left, int right)
{
	if (arr == NULL || left <0 || left> right)
		return;
	for (int i = 1; i <= right; i++)//正向检测
	{
		for (int j = 0; j <= right - i - 1; j++)
		{
			if (arr[j] >= arr[j + 1])
			{
				Swap(arr[j], arr[j + 1]);
			}
		}
	}

}
void BubbleSort2(int arr[], int left, int right) //反向检测
{
	//bool exchange;
	if (arr == NULL || left <0 || left> right)
		return;
	for (int i = 0; i < right; i++)
	{
		//exchange = 0;
		for (int j = right; j >=i+1 ; j--)
		{
			if (arr[j] <= arr[j-1])
			{
				Swap(arr[j], arr[j -1]);
				//exchange = 1;
			}
			//if (exchange == 0)
			//	return;
		}
	}

}
#endif

//######################################################
#ifndef __QUICKSORT_H__
#define __QUICKSORT_H__
#include <stdio.h>
#include <stdlib.h>
//将数组分为两部分,left全是小于key,right全是大于key,再递归将小数组在子问题
void Swap(int& a, int&b)
{
	int temp = a;
	a = b;
	b = temp;
}
/*
################################################################
快速排序是由东尼·霍尔所发展的一种排序算法。在平均状况下,排序 n 个项目要Ο(n log n)次比较。
在最坏状况下则需要Ο(n2)次比较,但这种状况并不常见。
事实上,快速排序通常明显比其他Ο(n log n) 算法更快,
因为它的内部循环(inner loop)可以在大部分的架构上很有效率地被实现出来,
且在大部分真实世界的数据,可以决定设计的选择,减少所需时间的二次方项之可能性。

步骤:

从数列中挑出一个元素,称为 “基准”(pivot),
重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。
在这个分区退出之后,该基准就处于数列的中间位置。这个称为分区(partition)操作。
递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序
########################################################################################
*/


int Partition1(int arr[], int left, int right)
{
	//左右指针法
	if (arr == NULL || left <0 || left>right)
		return -1;
	int begin = left;
	int end = right;
	int key = arr[right];
	while (begin < end)
	{
		while (arr[begin] <= key && begin <end)// begin从左 找大数,end从右找小数,然后交换
		begin++;
		while (arr[end]>=key && begin < end)
			end--;
		Swap(arr[begin], arr[end]);
	}
	    Swap(arr[begin], arr[right]);//最后begin与 key 值交换 使key左边得值小于key,key右边的值大于key
	return begin;

}


int Partition2(int arr[], int left, int right)
{
	//挖坑法
	//保存key值
	//利用两个指针,begin先走,找大,填到右边,end再走,找小,填到左边
	if (arr == NULL || left<0 || left> right)
		return -1;
	int begin = left;
	int end = right;
	int key = arr[right];
	while (begin < end)
	{
		while (arr[begin] <= key && begin<end)
			begin++;
		arr[end] = arr[begin];
		while (arr[end] >= key && begin < end)
			end--;
		arr[begin] = arr[end];
	}
	arr[begin] = key;
	return begin;

}

int Partition3(int arr[], int left, int right)
{
	//前后指针法
	//
	if (arr == NULL || left<0 || left> right)
		return -1;
	int cur = left;
	int prev = left-1;
	int key = arr[right];
	while (cur <= right)
	{
		if (arr[cur] <= key && arr[++prev] != arr[cur])
			Swap(arr[prev], arr[cur]);
		cur++;
	}
	return prev;

}

void QuickSort(int arr[], int left, int right)
{
	if (arr == NULL || left <0 || left>right)
		return;
	int index = Partition3(arr, left, right);
	if (index > left)
		QuickSort(arr, left, index - 1);
	if (index < right)
		QuickSort(arr, index + 1, right);
}
#endif

//####################################################
#ifndef __SELECTSORT_H__
#define __SELECTSORT_H__
#include "QuickSort.h"
#include <stdio.h>
#include <stdlib.h>

/*
#######################################################################################
快速排序是由东尼·霍尔所发展的一种排序算法。在平均状况下,排序 n 个项目要Ο(n log n)次比较。
在最坏状况下则需要Ο(n2)次比较,但这种状况并不常见。
事实上,快速排序通常明显比其他Ο(n log n) 算法更快,
因为它的内部循环(inner loop)可以在大部分的架构上很有效率地被实现出来,
且在大部分真实世界的数据,可以决定设计的选择,减少所需时间的二次方项之可能性。

步骤:

从数列中挑出一个元素,称为 “基准”(pivot),
重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。
在这个分区退出之后,该基准就处于数列的中间位置。这个称为分区(partition)操作。
递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序
########################################################################################
*/
void Select_Sort1(int arr[],int left,int right)  //冒泡思想
{
	//第一个数依次与后面的所有数进行比较,找最小数
	for (int i = left; i < right; i++)
	for (int j = i + 1; j <= right; j++)
	{
		if (arr[i]>arr[j])
			Swap(arr[i], arr[j]);
	}


}
//##############################################################
//选择排序
//1.在未排序的元素中找到最小的元素,,放在数组首
//2.在剩余未排序的元素中找最小数,续在已排好序的尾部

void Select_Sort2(int arr[], int left, int right)  //选择最小数下标,先保存第一个数的下标,
                                                   //在依次比较第一个数与其他数,保存最小数下标,交换
{
	for (int i = left; i <= right; i++)
	{
		int min = i;//保存下标
		for (int j = i+1; j<=right; j++)//判断
		{
			if (arr[min] > arr[j])
				min = j;
				
		}
		if (min != i){
			Swap(arr[min], arr[i]);
		}
	}
}


/*##############################################################

二叉堆满足二个特性:
1.父结点的键值总是大于或等于(小于或等于)任何一个子节点的键值。
2.每个结点的左子树和右子树都是一个二叉堆(都是最大堆或最小堆)。
当父结点的键值总是大于或等于任何一个子节点的键值时为最大堆。
当父结点的键值总是小于或等于任何一个子节点的键值时为最小堆。
堆的存储
一般都用数组来表示堆,i结点的父结点下标就为(i – 1) / 2。
它的左右子结点下标分别为2 * i + 1和2 * i + 2。如第0个结点左右子结点下标分别为1和2。
在堆的数据结构中,堆中的最大值总是位于根节点。堆中定义以下几种操作:
最大堆调整(Max_Heapify):将堆的末端子结点作调整,使得子结点永远小于父结点
创建最大堆(Build_Max_Heap):将堆所有数据重新排序
堆排序(HeapSort):移除位在第一个数据的根结点,并做最大堆调整的递归运算
*/    

               
void BuildHeap(int arr[], int parent ,int k )
{
	if (arr == NULL)
		return;
	int leftchild = parent * 2 + 1;
	int rightchild = parent * 2 + 2;
	while (leftchild < k)
	{
		if (rightchild<k && arr[rightchild]>arr[leftchild])
			++leftchild;
		if (arr[leftchild]>arr[parent])
		{
			Swap(arr[parent], arr[leftchild]);
			parent = leftchild;
			leftchild = parent * 2 + 1;
		}
		else
			break;
	}
}

void Heap_Sort(int arr[], int num,int left,int right)//堆排序
{
	//arr 待调整数组 right+1 为数组长度 num 为要调整的位置
    //建立一个堆
	//写堆的向下调整算法(交换堆顶和末尾元素)
	//输入数据,调整
	
	if (arr == NULL)
		return;
	for (int parent = (right + 1) / 2; parent >= 0; parent--)
		BuildHeap(arr, parent, right);

	for (int i = right; i > 0; i--)
	{
		Swap(arr[0], arr[i - 1]);
		BuildHeap(arr, 0, i - 1);
	}
}
#endif



#ifndef __MERGE_H__
#define __MERGE_H__
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
using namespace std;

/*
###############################################################
归并排序(Merge sort,台湾译作:合并排序)是建立在归并操作上的一种有效的排序算法。
该算法是采用分治法(Divide and Conquer)的一个非常典型的应用

步骤:

申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列
设定两个指针,最初位置分别为两个已经排序序列的起始位置
比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置
重复步骤3直到某一指针达到序列尾
将另一序列剩下的所有元素直接复制到合并序列尾
#################################################################
*/

//归并排序
//分治法,把一个N规模的问题划分为若干相同思路的小问题
//动态规划中有自顶向下和自底向上两种思路,这里是自底向上
//新开辟一个同样大小的空间作为中转
//1.分解,将当前区域一分为二   mid = (left+right)/2
//2.求解,递归子问题将区域划分为两个子问题
//3.当最后剩余两个时,进行比较,排序,归并
void Merge(int arr[],int temp[],int left,int mid, int right)//归并排序
{
	
	if (arr == NULL || left < 0)
		return;
	int begin1 = left;
	int begin2 = mid+1;
	int k = left ;
	//两段区域比较大小,依次把小值放在temp[]
	while (begin1 != mid+1 && begin2 != right+1)
	{
		if (arr[begin1] < arr[begin2])
			temp[k++] = arr[begin1++];
		else
			temp[k++] = arr[begin2++];

	}
	//处理一段区域内较长部分(可以用if判断,下面的两个循环只能进去一个)
	while (begin1 < mid+1)
		temp[k++] = arr[begin1++];
	while (begin2 <= right)
		temp[k++] = arr[begin2++];
	//将temp[]中的数拷贝回去
	for (int i = left; i <= right; i++)
		arr[i] = temp[i];

}

void MergeSort(int arr[], int temp[],int left, int right)
{
	if (arr == NULL)
		return;
	int mid;
	if (left < right)
	{
		mid = (left + right) / 2;
		MergeSort(arr,temp, left, mid);
		MergeSort(arr, temp,mid + 1, right);
		Merge(arr,temp,left, right, mid);
	}
}
#endif
//###############################################


#include "QuickSort.h"
#include "InsertSort.h"
#include "BubbleSort.h"
#include "SelectSort.h"
#include "MergeSort.h"

int main()
{
	int arr[10] = { 3, 5, 7, 1, 2, 8, 9, 5, 3, 0 };
	//int arr[10] = { 2, 2, 2, 4, 1, 4, 7, 5, 9, 0 };
	int temp[10];

	for (int i = 0; i <10; i++)
	{
		printf("%d ", arr[i]);
	}
	printf("\n");
	int left = 0;
	int right = sizeof(arr) / sizeof(arr[0])-1;
//####################################################
//测试函数
	//QuickSort(arr, left, right);
	//BubbleSort1(arr, left, right);
	//BubbleSort2(arr, left, right);
	//InsertSort(arr, left, right);
	//Select_Sort2(arr, left, right);
	//Heap_Sort(arr, left, right); 
	 MergeSort(arr, temp, left, right);
//####################################################
	for (int i = 0; i <=right; i++)
	{
		printf("%d ", arr[i]);
	}
	printf("\n");
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值