排序算法问题(二)堆排序,希尔排序
1.直接选择排序
核心思想:(假设从小到大排序)
每次循环将子序列中最小的数放在第一位;将问题逐渐变为n-1个数比较大小的问题(递归思想);这种排序属于确定位置找最小元素的问题。
==算法解决:==如何将最小的数放在第一位?
1.用函数计算数组(c/c++)或列表的长度(python(len))
2.控制每一个元素跟后边的子序列比较
for(i=0;i<length-1;i++)或者for i in range(0,length-1)j//减一是因为最后一个数无子序列跟它比较结果已经出来了。
3.假设第一个元素都是最小的(记录下标)min=i
4.循环遍历子序列与假设的最小值比较
for(j=i+1;j<length;j++)或者for j in range(i+1,length)
5.如果假设的最小值大于子序列的中的值,min更改为该值的下标(min=j)。如果最小值的下标不是该循环的第一个元素则互换元素。
if(min!=i)
{t=a[min];
a[min]=a[i];
a[i]=t;
}
或者(python)
List[i],List[min]=List[min],List[i]
For instance:(常规实现)
== 1.记录下标法==
#include<stdio.h>
void main()//主函数
{
int a[10];
int i,j,t,min;//min记录最小值下标
printf("请输入10个数字: \n");
for(i=0;i<10;i++)
scanf("%d",&a[i]);
for(i=0;i<10-1;i++)//最后一个数无子序列,无需比较即得到结果
{
min=i;
for(j=i+1;j<10;j++)//不用和自己比较所以j=1
//不能写成for(j=1;j<10-i;j++)因为min记录下标i,这样会使j无法对应下标
if(a[i]>a[j])
min=j;
if(min!=i)
{
t=a[i];
a[i]=a[min];
a[min]=t;
}
}
printf("排序后:\n");
for(i=0;i<10;i++)
printf("%4d",a[i]);
printf("\n");
}
2.直接换(此法跟冒泡法很类似,为了区分不建议使用)
#include<stdio.h>
void main()//主函数
{
int a[10];
int i,j,t;
printf("请输入10个数字: \n");
for(i=0;i<10;i++)
scanf("%d",&a[i]);
for(i=0;i<10-1;i++)
{
for(j=i+1;j<10;j++)
if(a[i]>a[j])//与冒泡法的不同之处在于冒泡是相邻两个元素比较大小互换。i实则就对应了最小值//不能写成for(j=1;j<10-i;j++)因为min记录下标i,这样会使j无法对应下标而冒泡法是前几个数的序列相邻两个数比较大小
{
t=a[i];
a[i]=a[j];
a[j]=t;
}
}
printf("排序后:\n");
for(i=0;i<10;i++)
printf("%4d",a[i]);
printf("\n");
}
(递归实现直接选择排序算法)
思想:与普通做法相同,普通做法本就包含递归思想,这里只不过将第一个for循环的形式改为递归实现。
#include<stdio.h>
#include<stdlib.h>
#define N 10
void SelectionSort(int* a,int n,int i)
{
if(i>=n-1)
{
return;
}
else{
int temp,j;
temp=i;
for(j=i+1;j<n;j++)
{
if(a[j]<a[temp])
temp=j;
}
if(temp!=i)
{
int m;
m=a[temp];
a[temp]=a[i];
a[i]=m;
}
SelectionSort(a,n,i+1);
}
}
int main()
{
int a[N],i,j,temp;
printf("请输入10个数:\n");
for(i=0;i<N;i++)
{
scanf("%d",&a[i]);
}
SelectionSort(a,N,0);
printf("排好序的数为:\n");
for(i=0;i<N;i++)
printf("%d ",a[i]);
printf("\n");
}
2.冒泡排序(从小到大)
核心思想:相邻两个元素比较大小并进行互换,一轮排序后最大的在最后(即最大的泡浮出水面)
剩下的子序列再两两比较,该循环结束后该子序列的最大值到次末尾。
For instance:
#include<stdio.h>
int main()//主函数
{
int a[10];
int i,j,t,flag;//min记录最小值下标
printf("请输入10个数字: \n");
for(i=0;i<10;i++)
scanf("%d",&a[i]);
for(i=0;i<10-1;i++)
{
flag=0;//假设没有元素交换
for(j=0;j<10-i-1;j++)//不能写成for(j=0;j<10-i;j++)原因在下一条语句
if(a[j]>a[j+1])//即第一次循环的j+1=10,造成数组溢出
{
t=a[j];
a[j]=a[j+1];
a[j+1]=t;
flag=1;
}
if(flag==0)//节省时间提前退出
break;
}
printf("排序后:\n");
for(i=0;i<10;i++)
printf("%4d",a[i]);
printf("\n");
return 0;
}
与直接选择排序的区别:相邻两个元素间比较大小,一轮排序后确认最大值在最后。而直接选择排序一轮比较后确认的最小值在前面,这就决定了为什么在第二个for循环两种排序j取值不同的原因。
3.插入排序(从小到大)
==核心思想:==默认第一个有序,依次提取后面的数(放于暂存空间)跟前面的有序数据比较,如果比前面的数据小,则有序数据往后移动,否则在特定位置插入该元素。类似斗地主抓牌的时候整理牌的过程。
#include<stdio.h>
void InsertionSort(int *num,int n)
{
int i,j, temp = 0;
for(i = 1;i<n;i++)//从第二个数开始和前面比较
{
temp = num[i];//从待插入组取出第一个元素放到到暂存空间
for(j=i;j>0&&temp<num[j-1];j--)//该元素依次跟前面的数比较,如果小于前面的数则前面的有序数依次往后移动
{
num[j]=num[j-1];//
}
num[j]=temp;//该元素插入到合适的位置
}
}
int main()
{
int i;
int num[8];
printf("请输入8个数据:\n");
for(i=0;i<8;i++)
scanf("%d",&num[i]);
InsertionSort(num,8);
for(i=0;i<8;i++)
printf("%d ",num[i]);
return 0;
}
4.快速排序(分治思想)
分治思想:将一个大问题拆分为足够小的小问题(递归思想),再将小问题求解,然后自底向上合并得到大问题的解。
核心思想:分治思想是均分,而快排根据哨兵的位置分,而最初哨兵位置的选取,决定了算法的性能的高低,一般哨兵最终位置在数列的中间性能最佳,即均分两个子序列,因此哨兵的位置因采取随机选取的方式较好,正因如此快速排序可以有多种优化方式。(i,j相遇时就是哨兵的最终位置),分完之和对两个子序列进行排序,排完后不需要合并。
1.先从数列中取出一个数作为基准数。
2.分区过程,将比这个数大的数全放到它的右边,小于或等于它的数全放到它的左边。
3.再对左右区间重复第二步,直到各区间只有一个数(分治)。
快速排序(挖坑版)
特定选取某个元素作为哨兵。(第一个位置)
For instance:
#include<stdio.h>
//快速排序
#define N 5
int quick_sort(int s[], int l, int r)
{
int i = l, j = r, x = s[l];
while (i < j)
{
while(i < j && s[j] >= x) // 从右向左找第一个小于x的数
j--;
if(i < j)
s[i++] = s[j];//将小于x的数放于左边
while(i < j && s[i] < x) // 从左向右找第一个大于等于x的数
i++;
if(i < j)
s[j--] = s[i];//将大于x的数放于右边
}
s[i] = x;
return i;
}
//分治
void quick_sort1(int s[], int l, int r)
{
if (l < r)
{
int i =quick_sort(s, l, r);//先成挖坑填数法调整s[]
quick_sort1(s, l, i - 1); // 递归调用
quick_sort1(s, i + 1, r);
}
}
int main(){
int num[N],left=0,rigth=N-1,i,x;
x=N;
printf("请输入%d个整数!\n",x);
for(i=0;i<N;i++)
scanf("%d",&num[i]);
quick_sort1(num,left,rigth);
for(i=0;i<N;i++)
printf("%d ",num[i]);
}
快速排序(交换版)
#include<stdio.h>
void Swap(int arr[], int low, int high)
{
int temp;
temp = arr[low];
arr[low] = arr[high];
arr[high] = temp;
}
int Partition(int arr[], int low, int high)
{
int base = arr[low];
while(low < high)
{
while(low < high && arr[high] >= base)
{
high --;
}
Swap(arr, low, high);
while(low < high && arr[low] <= base)
{
low ++;
}
Swap(arr, low, high);
}
return low;
}
void QuickSort(int arr[], int low, int high)
{
if(low < high)
{
int base = Partition(arr, low, high);
QuickSort(arr, low, base - 1);
QuickSort(arr, base + 1, high);
}
}
int main()
{
int n;
scanf("%d\n",&n);
int arr[n];
int i , j;
for(i = 0; i < n; i ++)
{
scanf("%d",&arr[i]);
}
printf("\n");
QuickSort(arr, 0, n-1);
for(j = 0; j < n; j ++)
{
printf("%4d",arr[j]);
}
return 0;
}
5.归并排序
核心思想:采用分治思想将数据先递归的分解,再合并数数据就完成了归并排序。将数据分成两组,以此类推,当分出来的小组只有一个数据时,可以认为这个小组组内已经达到了有序,然后再合并相邻的二个小组就可以了。
For instance:
#include<stdio.h>
#include<time.h>
#include<stdlib.h>
#define N 5
//将有二个有序数列a[first...mid]和a[mid...last]合并
void mergearray(int a[], int first, int mid, int last, int temp[])
{
int i = first, j = mid + 1;
int m = mid, n = last;
int k = 0;
while (i <= m && j <= n)
{
if (a[i] <= a[j])
temp[k++] = a[i++];
else
temp[k++] = a[j++];
}
while (i <= m)
temp[k++] = a[i++];
while (j <= n)
temp[k++] = a[j++];
for (i = 0; i < k; i++)
a[first + i] = temp[i];
}
void mergesort(int a[], int first, int last, int temp[])
{
if (first < last)
{
int mid = (first + last) / 2;
mergesort(a, first, mid, temp); //左边有序
mergesort(a, mid + 1, last, temp); //右边有序
mergearray(a, first, mid, last, temp); //再将二个有序数列合并
}
}
bool MergeSort(int a[], int n)
{
int *p = new int[n];
if (p == NULL)
return false;
mergesort(a, 0, n - 1, p);
delete[] p;
return true;
}
int main()
{
int arr[N];
int i ,x, j;
x=N;
printf("请输入%d个整数:\n",x);
for(i = 0; i < N; i ++)
{
scanf("%d",&arr[i]);
}
printf("\n");
MergeSort(arr, N);
for(j = 0; j < N; j ++)
{
printf("%4d ",arr[j]);
}
return 0;
}