排序算法学习笔记2(希尔、归并、快速Java)
一、希尔排序
希尔排序为改良版的插入排序,主要的排序核心也是插入排序,再次基础上跳着插入排序,需要加入一个间隔gap俩间隔的插入排序,间隔的选择使用的是h = 3 * h + 1的间隔数列,写这个排序算法的思路就是先写一个普通的插入排序的方法,之后再见普通插入排序的间隔为1换成自己要的间隔gap就可以了,之后就是使得从间隔起的后面每个数都组成的一个新的间隔数列都可以被进行一次插入排序,这种改良插入排序比原本的插入排序效率高上一些。但是不稳定
public class Shell_sort {
public static void main(String[] args) {
//自己设定的数组
int []arr = {7,9,3,2,1,6,5,25,42,12};
//间隔一直减少,最后一次一定要间隔为1
int gap = 1;
//利用这个循环找到找到最大间隔,从最大间隔开始减少
while(gap <= arr.length/3)
{
gap = gap * 3 + 1;
}
//多次调用插入方法,不同的间隔都得进行一次插入排序
for(; gap > 0;gap = (gap-1)/3)
{
Insert_Sort(arr,gap);
}
print(arr);
}
public static void Insert_Sort(int []arr,int h) {
for(int i = h;i < arr.length;i++)
{
for(int j = i;j - h >= 0;j -= h)
{
if(arr[j] < arr[j-h])
{
swap(arr,j,j-h);
}
}
}
}
public static void swap(int []arr,int i,int j) {
int tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
public static void print(int []arr) {
for(int i = 0;i < arr.length;i++)
{
System.out.print(arr[i] + " ");
}
}
}
二、归并排序
归并排序使用到递归的思想,但是归并的过程的函数很简单,就是一个数组两部分是相对有序,对比合并入一个新的数组中,但是一般给定的数组都是无序的,不符合合并的过程函数要求的相对有序,那就可以分开,将对半分开的‘两个数组’,如果不相对有序,就再分,直到分到一个数组中只有一个数字或者两个数字时候,就停止分,这时候就可以将相对有序的两小部分归并,归并后返回就逐步扩大有序的部分,这就是递归过程,递归过程发生在分类中,而不是发生在归并过程。这是一个稳定的排序算法,Java内部使用的排序也是归并排序,但是用到的是改良版的归并排序,TimSort,其中用到了归并排序和插入排序。
public class Merge_sort {
public static void main(String[] args) {
int []arr = {7,9,3,2,1,6,5,25,42,12};
//int []arr = {1,3,5,7,2,4,9};
sort(arr,0,arr.length-1);
print(arr);
}
public static void sort(int []a,int begin,int end) {
if(begin >= end) return;
int mid = begin + (end - begin)/2;
sort(a,begin,mid);
sort(a,mid+1,end);
merge(a,begin,mid+1,end);
}
public static void merge(int []arr,int leftP,int rightP,int end) {
//辅助空间
int []tmp = new int[arr.length];
int i = leftP;
int j = rightP;
int k = leftP;
while(i < rightP&&j <= end)
{
if(arr[i] <= arr[j])
{
tmp[k++] = arr[i++];
}
else
{
tmp[k++] = arr[j++];
}
}
//归并过程中有其中一部分是比较后还是长出来的一部分,直接录入数组就可以了
if(i < rightP)
{
while(i < rightP)
{
tmp[k++] = arr[i++];
}
}
if(j <= end)
{
while(j <= end)
{
tmp[k++] = arr[j++];
}
}
//排序成功后的数组是临时变量,要放入原来的数组中,否则打印无效
for(int c = leftP;c <=end;c++)
{
arr[c] = tmp[c];
}
}
public static void print(int []arr) {
for(int i = 0;i < arr.length;i++)
{
System.out.print(arr[i] + " ");
}
}
}
三、快速排序
快速排序我这里学习的是简单的单轴快排,还有改良的双轴快排,后续会继续写上,快速排序也有用到递归排序,算法写的过程可以拆解为两个部分,比较交换部分,分类递归部分,这个算法十分容易出现bug,主要是边界问题的检查,多调试几次就可以写出正确的代码,轴的确立在快速排序中非常重要,首先以整个数组的最末尾位置作为轴,从0位置以及轴前面一个的位置开始寻找,从前往后找比轴大的第一个数和从后往前找第一个比轴小的数,依次类推逐个寻找交换,直至两个指针相等或者交错,这也是交换比较循环的结束条件,循环结束后还要处理一下轴这个数字,在每次循环结束之后的左指针位置和轴的位置交换即可。
其次是递归部分,在比较后返回轴交换后的位置,以轴的位置划分为两个数组,再以每个数字的末尾作为轴来进行比较交换。
public class quick_sort {
public static void main(String[] args) {
//int []arr = {7,9,3,2,1,6,5,25,42,12};
int []arr = {100,25,32,111,5252,363,1,0,1,0};
//compare(arr, 0, arr.length-1);
sort(arr, 0, arr.length-1);
print(arr);
}
public static void sort(int []arr,int begin,int end) {
if(begin >= end) return ;
int mid = compare(arr,begin,end);
sort(arr,begin,mid-1);
sort(arr,mid+1,end);
}
public static int compare(int[]arr,int left,int rightP) {
if(left >= rightP) return 0;
int p = rightP;//轴的位置
int right = rightP - 1;
while(left <= right)
{
while(left <= right && arr[left] <= arr[p])
left++;
while(left <= right && arr[right] > arr[p])
right--;
if(left <= right)
swap(arr,left,right);
}
swap(arr,left,p);
// print(arr);
// System.out.println();
// System.out.println(left);
// System.out.println();
return left;
}
public static void swap(int []arr,int i,int j) {
int tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
public static void print(int []arr) {
for(int i = 0;i < arr.length;i++)
{
System.out.print(arr[i] + " ");
}
}
}