数据结构-八大排序--java实现

八大排序–java实现

最近在做面试题,遇到Partition函数,看剑指offer并没看懂,于是自己琢磨,感觉很熟悉,于是决定看下排序算法,发现这就是快速排序。写次博客,也方便以后自己查找简单。

冒泡排序

冒泡排序最为简单,学C语言,首先就是学会冒泡排序。自我理解:
从第一个开始,如果前一个大于后一个,两两交换,则会使得最大的数一直向后移。遍历完一遍后,最大的数就是最后一个数。然后从头开始,遍历除了最后一个以外的数,则第二个数也会沉底。以此类推,完成排序。
整个过程看起来就像是一连串泡泡,逐渐变大,所以称作冒泡排序。

package sort;
//冒泡排序,最为熟悉的排序
public class Bubble_sort {

	private void Bubble_sort(int[] a) {
		// TODO Auto-generated constructor stub
		for(int i=0;i<a.length-1;i++) {
			for(int j=0;j<a.length-i-1;j++) {
				if(a[j]<a[j+1]) {
					int temp =a[j];
					a[j] = a[j+1];
					a[j+1] = temp;
				}
			}
		}
	}

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		int[] a = new int[]{9,6,4,5,7,3,2};
		Bubble_sort s= new Bubble_sort();
		s.Bubble_sort(a);
		for(int i=0;i<a.length;i++) {
			System.out.print(a[i]+",");
		}
	}

}

插入排序

插入排序和冒泡排序有异曲同工之妙。插入排序:将一个数插入到其合适的位置。
默认第一个数为排序好的数,若第二个数大于他就放在其后,小于它则放在它前面。
第三个数则在前面两个数中选择合适位置进行插入。相应的数字向后移即可。
以此类推,完成数组排序。

package sort;
//插入排序
public class insert_sort {

	public static void insert_sort(int[] a) {
		// TODO Auto-generated constructor stub
		for(int i=0;i<a.length;i++) {
			int num = a[i];
			int j;
			for(j=i;j>0&&num<a[j-1];j--) {
				a[j] = a[j-1];//数组后移
			}
			a[j] =num;//找到位置,插入进去
		}
	}

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		int[] a = new int[]{9,6,4,5,7,3,2};
		insert_sort(a);
		for(int i=0;i<a.length;i++) {
			System.out.print(a[i]+",");
		}	
	}
}

希尔排序

希尔排序是在插入排序的基础上选择步长进行插入。我的理解:
设定步长h,间隔为步长的n/h个数进行插入排序,则此n/h个数已经完成排序。循环后移,完成所有间隔为h的数排序。
减小步长,直至减为1,进行插入排序。
希尔排序相比插入排序,减少了遍历的次数,提高了相率。后续进行学习复杂度再来比较其真正的不同。

package sort;

public class Shell_sort {

	public static void Shell_sort(int[] a) {
		// TODO Auto-generated constructor stub
		int h= a.length/2;
		for(;h>=1;h/=2) {
			for(int i=0;i<a.length;i+=h) {
				int num = a[i];
				int j;
				for(j=i;j>0 && num<a[j-h];j-=h) {
					a[j] = a[j-h];//数组后移
				}
				a[j]=num;
			}
		}
	}

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		int[] a = new int[]{9,6,4,5,7,3,2,1,4,9,50,20,32};
		Shell_sort(a);
		for(int i=0;i<a.length;i++) {
			System.out.print(a[i]+",");
		}
	}

}

选择排序

选择排序的思想比较简单:从数组中选取最小的数将其放在起始位置,逐步选取,然后填充即可。

package sort;

public class Selection_sort {

	public static void Selection_sort(int[] a) {
		// TODO Auto-generated constructor stub
		int min;
		for(int i=0;i<a.length-1;i++) {
			min = i;
			for(int j=i+1;j<a.length;j++) {
				if(a[min]>a[j]) {
					min = j;
				}
			}
			if(a[min]!=a[i]) {
				int temp = a[i];
				a[i] = a[min];
				a[min] = temp;
			}
		}
	}

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		int[] a = new int[]{9,6,4,5,7,3,2,1,4,9,50,20,32};
		Selection_sort(a);
		for(int i=0;i<a.length;i++) {
			System.out.print(a[i]+",");
		}
	}
}

以上四个排序算法比较简单,看一看文档以及相关博客即可理解思想,完成编程。
下面四个算法相对比较难,多思考,看着程序想想实现,就会明白的。

快速排序

快速排序就是开头说的partition函数。其中心思想为:
选定一个数,比它小的放前面,比它大的放后面。再分为对小数组和大数组进行递归快速排序,直至完成所有数字排序。
如何实现:
选取两个指针(一个从前向后遍历,一个从后向前遍历),一个基数 m, 找到第一个比它小的数,指针停留,找到第一个比它大的指针,两个指针所指向的数进行交换,然后指针再次移动,直至两个指针相遇,于是我们找到了m所对应的位置,进行交换。

生动形象的一个介绍快速排序的文档:生动形象的快速排序
快速排序的优化可针对基数的选取进行优化,不进行随机选取,而选择中间的数进行优化。

package sort;
//递归版本的快速排序
public class Quick_gui_sort {

	public static void Quick_sort(int[] a,int low,int high) {
		// TODO Auto-generated constructor stub
		if(low>=high) {
			return;
		}
		int left = low;
		int right = high;
		int pri = a[left];
		/*while (left < right) {
			while (left < right && a[right] >= pri) {
				right--;
			}
			a[left] = a[right];
			while (left < right && a[left] <= pri) {
				left++;
			}
			a[right] = a[left];
		}
		a[left] = pri;
		Heap_sort(a, low, left - 1);
		Heap_sort(a, left + 1, high);*/
		while(left<right) {
			while(left<right && a[right]>=pri) {
				right--;
			}
			while(left<right && a[left]<=pri) {
				left++;
			}
			if(left<right) {
				//找到了需要交换的数
				int temp = a[left];
				a[left] = a[right];
				a[right] = temp;
			}
		}
		//将pri放在他应该在的位置
		int temp = a[left];
		a[left] = pri;
		a[low] = temp;
		//继续排序前面一个数组和后面一个数组
		Quick_sort(a, low, left - 1);
		Quick_sort(a, left + 1, high);
	}

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		int[] a = new int[]{9,6,4,5,7,3,2};
		Quick_sort(a,0,a.length-1);
		for(int i=0;i<a.length;i++) {
			System.out.print(a[i]+" ");
		}
	}
}

基数排序

基数排序是非常有趣的一种排序,据说一般不会问到,但我认为基数排序比较有趣,于是写了下来。
基数排序的理解:
找到最大的数,获取其位数,将比它小的数前面填充0。然后对每个数的每一位进行遍历。这里就需要一个桶,用来存放某个位数上相同的数。
先遍历个位,将各位相同的放入一个桶中,输出数组,在对十位进行排序,逐位排序,输出。遍历完之后就得到了最终的排序结果。

package sort;

import java.util.ArrayList;
import java.util.List;

public class radix_sort {
    public static void main(String[] args) {
        int[] a = {542, 3521, 13459, 852, 742, 46, 2, 1, 633, 32};
        System.out.println("Before Sort the Array is:");
        for (int array : a
                ) {
            System.out.print(array + " ");
        }
        // 调用基数排序
        myRadixSort(a);
        System.out.println();
        System.out.println("After Sort the Array is:");
        for (int array : a
                ) {
            System.out.print(array + " ");
        }
    }

    public static void myRadixSort(int[] arr) {
    	//获取数组的最大数
    	int max = 0;
    	for(int i=0;i<arr.length;i++) {
    		if(max<arr[i]) {
    			max = arr[i];
    		}
    	}
    	//确定最大数的位数
    	int time = 0;
    	while(max>0) {
    		max = max/10;
    		time++;
    	}
    	//初始化十个桶
    	ArrayList<ArrayList> num_list = new ArrayList();
    	for(int i=0;i<10;i++) {
    		ArrayList<Integer> list1 = new ArrayList();
    		num_list.add(list1);
    	}
    	
    	for(int i=0;i<time;i++) {
    		//对应数字存入对应桶中
    		for(int j=0;j<arr.length;j++) {
    			int x = (arr[j] % (int)Math.pow(10, i+1) / (int)Math.pow(10, i));
    			num_list.get(x).add(arr[j]);
    		}
    		
    		//从桶中取出数,然后进行递归高位排序
    		int index=0;
    		for(int j=0;j<10;j++) {
    			while(num_list.get(j).size()>0) {
    				ArrayList<Integer> sma_list = num_list.get(j);
    				arr[index] = sma_list.get(0);
    				sma_list.remove(0);
    				index++;
    			}
    		}	
    	}	
    }
}

归并排序

归并排序是指一种分而治的思想,和快速排序有点相似。快速排序是从大范围向小范围的并。递归排序是小范围向大范围的并。
我的理解:将数组进行细分,分到单一数组之后,进行两两排序合并,逐渐合并到原来数组,完成排序。
思想:
(1)申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列
(2)设定两个指针,最初位置分别为两个已经排序序列的起始位置
(3)比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置
(4)重复步骤3直到某一指针达到序列尾
(5)将另一序列剩下的所有元素直接复制到合并序列尾

推荐文档: link.

归并排序有两种实现方式:
1. 递归法
2. 迭代法
代码如下

package sort;
//归并排序的递归实现
import java.util.Arrays;

public class Gb_sort {

	public  void Gb_sort(int[] a,int low,int high) {
		// TODO Auto-generated constructor stub
		int mid = low+(high-low)/2;
		if(low<high) {
			Gb_sort(a,low,mid);
			Gb_sort(a,mid+1,high);
			merge(a,low,mid,high);
		}
	}
	private  void merge(int[] a,int low,int mid,int high) {
		int left = low;
		int right = mid+1;
		int index = 0;
		int[] t = new int[high-low+1];
		while(left<=mid && right<=high) {
			//分为之后,根据大小填入数组t
			if( a[left]<a[right]) {
				t[index++] = a[left++];	
			}else {
				t[index++] = a[right++];
			}
		}
		//将多余的数填入数组t
		while(left<=mid) {
			t[index++] = a[left++];
		}
		while(right<=high) {
			t[index++] = a[right++];
		}
		for(int k=0;k<t.length;k++) {
			a[low+k]=t[k];
		}
		
		System.out.println("temp::"+Arrays.toString(t));
	}
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		int[] a = new int[]{9,6,4,5,7,3,2};
		Gb_sort g= new Gb_sort();
		g.Gb_sort(a,0,a.length-1);
		for(int i=0;i<a.length;i++) {
			System.out.print(a[i]+",");
		}
	}

}

package sort;
 
import java.util.Arrays;
 
//迭代的方式进行  归并排序
public class Gb_iter_sort {
 
	private static void gb_iter_sort(int[] a,int low,int high) {
		int left_begin = 0;
		int left_end = 0;
		int right_begin = 0;
		int right_end = 0;
		int[] temp = new int[high-low+1];
		int index=0;
		for(int i=low;i<high;i*=2) {
			//low传入的1,所以不会影响
			for(left_begin = 0;left_begin<high-i;left_begin=right_end) {
				//必须添加“left_begin = 0”,如果不添加,当步长为1的循环跳出后,left_begin=high-2(因为high=a.length),
				//不会进入接下来的循环
				left_end = left_begin+i;
				right_begin = left_begin+i;
				right_end = right_begin+i;
				
				if(right_end>high) {
					right_end = high;
				}
				while(left_begin<left_end && right_begin<right_end) {
					//这里之所以不采用<=,是因为left_end = left_begin+i;
					//right_begin = left_begin+i;right_end = right_begin+i;
					//end超出选取数组的范围
					if(a[left_begin]<a[right_begin]) {
						temp[index++] = a[left_begin++];
					}else {
						temp[index++] = a[right_begin++];
					}
				}
				if(left_begin<left_end) {
					temp[index++] = a[left_begin++];
				}
				if(right_begin<right_end) {
					temp[index++] = a[right_begin++];
				}
				
				while(index>0) {
					//即控制了index,又省去了右边多余数组在添加一遍的时间复杂度
					a[--right_begin] = temp[--index];
				}
				System.out.println("temp:"+Arrays.toString(temp));
			}
		}
	}
	
	
	
	public static void main(String[] args) {
 
		
//		int[] arr={5,7,1,9,2,6,3,4};
		int arr[] = { 5, 2, 6, 0, 3, 9, 1, 7, 4, 8 };
		System.out.println("迭代合并--排序前:"+Arrays.toString(arr));
		gb_iter_sort(arr, 1, arr.length);
		System.out.println("迭代合并--排序后:"+Arrays.toString(arr));
		
	}
 
}

堆排序

堆排序是我认为八大排序里面最难的排序,所以放在最后面分析。看了许多博客都没太看懂,所以上B站找了视频,幸运的是,第一个就明白了,分享给大家。:堆排序的详细解释
经过学习后,明白了那些博客上写的,先建堆,然后对堆进行排序,我相信看了视频的你会理解的。

package sort;
/**
 * 堆排序演示
 *
 */
public class Heap_sort {
    public static void main(String[] args) {
        int[] tree = {5, 1, 7, 3, 1, 6, 9, 4};
        //int[] tree = {16, 7, 3, 20, 17, 8};
        //heapity(tree, tree.length, 0);
        //build_heap(tree, tree.length);
        heapSort(tree,tree.length);
        for (int i : tree) {
            System.out.print(i + " ");
        }
    }
    
    /*
	*排序,先进行建堆,建好之后的堆符合根节点最大,叶子节点小于根节点
	*之后我们将最后一个节点与最大的节点(根节点)进行交换,置换之后的字树也会进行随之改动。
	*最后进行递归,即可完成堆排序。
	*/
    private static void heapSort(int[] tree,int n) {
    	build_heap(tree, n);
    	for(int i=n-1;i>=0;i--) {
    		swap(tree,i,0);
    		heapity(tree, i, 0);
    	}
    	
    }
    /*
     * 调整堆
     * 1.获取左叶子节点和右子节点。取三者中的最大值放入根节点。
     * 2.若是需要进行调换,则进行递归,保证交换之后的子树也符合最大堆的原则。而不交换的子树不进行改变。
     */
    private static void heapity(int []tree,int n,int i) {
    	if(i>=tree.length) {
    		return;
    	}
    	int lchild = i*2+1;
    	int rchild = i*2+2;
    	int max = i;
    	
    	if(lchild<n && tree[lchild] > tree[max]) {
    		max = lchild;
    	}
    	if(rchild<n && tree[rchild] > tree[max]) {
    		max = rchild;
    		
    	}
    	
    	if(max!=i) {
    		swap(tree,max,i);
    		heapity(tree, n,max);
    	}
    }
    private static void swap(int[] tree,int i,int max) {
    	int temp = tree[i];
    	tree[i] = tree[max];
    	tree[max] = temp;
    }
    
    /*
     * 建立堆
     * 使整个二叉树符合堆:
     * 		从下向上的递归,即可保证最大堆的建立。
     * */
    private static void build_heap(int[] tree, int length) {
    	int last_node = length-1;//最后一个节点
    	int parent = (last_node-1)/2;//最后一个根节点
    	for(int i=parent;i>=0;i--) {
    		heapity(tree, length, i);
    	}
    }
}

感觉排序算法还是很多没搞明白,接下来要对每个算法的时间复杂度和空间复杂度进行分析,力求透彻。加油!!!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值