#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;
}
各类算法实现(sort)
最新推荐文章于 2023-11-10 20:26:26 发布