八大排序–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);
}
}
}
感觉排序算法还是很多没搞明白,接下来要对每个算法的时间复杂度和空间复杂度进行分析,力求透彻。加油!!!