学习记录:排序专题

1. 直接插入排序

顾名思义,实现排序的方式就是插入。下面由从小到大的顺序进行一次排序。

  1. 确定一个已经有序的序列,这里先选第一个元素为有序(毕竟只有一个元素)

  2. 从无序的第一个元素开始比较,有两种情况:

    1. 无序序列的第一个元素大于有序数列的最后一个元素,就说明这个元素在有序序列中是最大的,所以它的位置是正确的,无需任何操作。
    2. 无序序列的第一个元素小于有序数列的最后一个元素,该元素位置错误,从有序序列开始遍历,直到找到正确的位置。
      将已经遍历过的元素向后移一位,将该元素插入。

重复这种操作,直到全部有序

//代码如下
void InsertSort(int *arr,int length)
{
	int Temp;//用于记录无序序列的第一个元素
	for (int i=1;i<length;i++)
	{
		if (arr[i-1]<=arr[i])//第一种情况
			continue;
		else//第二种情况
		{
			Temp=arr[i];//记录
			for (int j=0;j<i;j++)
			{
				if (Temp<arr[j])//找到正确的位置
				{
					for (int k=i;k>=j;k--)//该循环用于将数组后移
						arr[k]=arr[k-1];
					arr[j]=Temp;//将该元素插入
					break;//退出循环
				}
			}
		}
	}
}
void InsertSort(int *arr,int length)
{
	int Temp,j;
	for (int i=1;i<length;i++)
	{
		Temp=arr[i];
		if (arr[i-1]>Temp)
		{
			for (j=i-1;Temp<arr[j]&&j>=0;j--)
				arr[j+1]=arr[j];
			arr[j+1]=Temp;
		}
	}
	for (int i=0;i<length;i++)
		cout<<arr[i]<<" ";
}

2. 折半插入

折半插入排序是对于插入排序的一个改良版本,将寻找适合插入位置的过程改为了运用二分来进行查找,相比直接插入排序要快一点

void Binary_InsertSort(int arr[],int length)
{
    for (int i=1;i<length;i++)
	{
		int left=0,right=i-1,m;
		while (left<=right) //折半查找寻找正确位置
		{
			m=(left+right)/2;
			if (arr[m]>arr[i])
				right=m-1;
			else
				left=m+1;
		}
		int temp=arr[i];
		for (int j=i;j>right+1;j--) //后移
			arr[j]=arr[j-1];
		arr[right+1]=temp; //插入
	}
}

3. 二路插入排序

二路插入排序是对折半插入的进一步改进,减少了交换的次数,其实现方法是多开一个同样大小的数组,再将它想象为一个循环的数组(类似于约瑟夫环),再利用两个指针(并不是真的指针,只是指明数的位置)记录在新建的数组中的最大数与最小数,来减少交换次数。

插入排序:二路插入,这一篇博客写的很好

void InsertSort2(int *a,int n)
{
	int first=0,last=0;
	int *b=new int[n];
	b[0]=a[0];
	for (int i=1;i<n;i++)
	{
		if (a[i]<=b[first])
		{
			first = (first-1+n)%n;
			b[first]=a[i];
		}
		else if (a[i]>=b[last])
		{
			last++;
			b[last]=a[i];
		}
		else
		{
			int left=first, right=last,m,tem;
			while (left!=right) //折半查找寻找正确位置
			{
				tem=(right-left+n)%n;
				m=(left+tem/2)%n;
				if (a[i] < b[m])
					right=m;
				else
					left=(m+1)%n;
			}
			for (int j=last+1;j!=left;j=(j-1+n)%n)
				b[j]=b[(j-1+n)%n];
			b[left] = a[i];
			last++;
		}
	}
	for (int i=0;i<n;i++)
		a[i]=b[(i+first)%n];
	delete []b;
}

4. 希尔排序

void ShellSort(int *arr,int length)
{
    for(int gap=length/2;gap>0;gap/=2)
    {
        for(int i=gap;i<length;++i)
        {
            int j=i;
            while(j-gap>=0 && arr[j-gap]>arr[j])
            {
            	swap(arr[j],arr[j-gap]);
                j=j-gap;
            }
        }
    }
}

5. 冒泡排序

void BubbleSort(int *arr,int length)
{
	for (int i=length-1;i>0;i--)
	{
		for (int j=0;j<i;j++)
		{
			if (arr[j]>arr[j+1])
				swap(arr[j+1],arr[j]);
		}	
	}	
}

6. 改进冒泡排序

void newBubbleSort(int *arr,int length)
{
	int flag=length,k;
	for (int i=0;i<flag;i++)
	{
		k=flag;
		flag=0;
		for (int j=0;j<k;j++)
		{
			if (arr[j]<arr[j+1])
			{
				flag=j;
				swap(arr[j],arr[j+1]);
			}
		}
	}
}

7. 鸡尾酒排序

void Cocktail_Sort(int *arr,int length)
{
	int low=0;
	int high=length;
	int flag=1,bound=1;
	while (flag)
	{
		flag=0;
		for (int i=low;i<length;i++)
		{
			if (arr[i]>arr[i+1])
			{
				swap(arr[i],arr[i+1]);
				flag=1;
				bound=i;
			}
		}
		high=bound;
		for (int i=high;i>low;i--)
		{
			if (arr[i]<arr[i-1])
			{
				swap(arr[i],arr[i-1]);
				flag=1;
				bound=i;
			}
		}
		low=bound;
	}
}

8. 快速排序

void Quick_Sort(int *arr,int beginn,int endn)
{
	if (beginn<endn)
	{
		int left=beginn;
		int right=endn;
		int mid=arr[beginn];
		while (left<right)
		{
			while (left<right&&arr[right]>=mid)
				right--;
			if (left<right)
				arr[left++]=arr[right];
			while (left<right&&arr[left]<=mid)
				left++;
			if (left<right)
				arr[right--]=arr[left];
		}
		arr[left]=mid;
		Quick_Sort(arr,beginn,left-1);
		Quick_Sort(arr,right+1,endn);
	}
}

9. 简单选择排序

初见感觉这个算法异常的暴力。核心思想就是遍历一遍找最大值or最小值,重复执行的同时范围也在随之减少(因为每遍历一趟减少了最值),直到数组有序。

概括一下:每一趟在后面 n-i 个待排记录中选取关键字最小的记录作为第 i 个值得记录,两层循环O(n^2)时间复杂度结束

void Simple_Select_Sort(int *arr,int istart,int iend)
{
	for (int i=0;i<iend-1;i++)
	{
		int temp=i;
		for (int j=i;j<iend;j++)
			if (arr[j]<arr[temp])
				temp=j;
		swap(arr[temp],arr[i]);
	}
}

10. 堆排序

堆有点二叉树的意思,而且我觉得在之前所有基础排序算法之中都是比较难的一种算法。

专门写了篇博客记录一下,顺带学了学怎么做示意图~堆排序简介

void HeapAdjust(int *arr,int top,int range)//arr为数组首地址,top为需要进行恢复的元素下标,range就是范围啦
{
	for (int i=top*2;i<=range;i*=2)//为什么是*2?因为要获得子节点的下标
	{
		if (i<range&&arr[i]<arr[i+1])
			i++;//找到两个子节点中最大的元素的下标
		if (arr[top]>=arr[i])
			break;//不需要交换就退出
		swap(arr[top],arr[i]);//较大的子节点上移给父节点
		top=i;//现在要处理的点就是子节点
	}
}
void InitHeap (int *arr,int length)
{
	for (int i=length;i>=1;i--)
		arr[i]=arr[i-1];
	for (int i=length/2;i>0;i--)
		HeapAdjust(arr,i,length);
}
void HeapSort(int *arr,int length)
{
	InitHeap(arr,length);
	for (int i=length;i>0;i--)
	{
		swap(arr[i],arr[1]);
		HeapAdjust(arr,1,i-1);
	}
	for (int i=0;i<length;i++)
		arr[i]=arr[i+1];
}

PS

int main()
{
	int length=20;
	int arr[length];
	srand((unsigned)time(NULL));
	clock_t start = clock();  //时间起始
	for (int i=0;i<length;i++)
	{
		arr[i]=rand()%100;
		printf("%5d",arr[i]);
	}
	printf("
");
	ShellSort(arr,length);
	clock_t end   = clock(); //时间测试结束
	cout<<end - start<<endl; //计算打印出运行时间,单位ms
	for (int i=0;i<length;i++)
		printf("%5d",arr[i]);
	return 0;
}
原文地址:https://www.cnblogs.com/Salty-Fish/p/12469385.html