不得不服!Java实现数据结构中的八种排序方法

  • @param data

  • @return

*/

public void bubbleSort(int[] data){

int i, j;

for(i=0;i<data.length-1;i++){

	boolean flag = true;//用于判断本次冒泡是否进行了交换

	for(j=0;j<data.length-i-1;j++){//第i次排序时最后i个元素已经有序

		if(data[j]>data[j+1]){

			swap(data, j, j+1);

			flag = false;

		}

	}

	if(flag){

		break;//某趟冒泡没有交换则可以结束排序

	}

}

System.out.println(Arrays.toString(data));

}




[](https://gitee.com/vip204888/java-p7)3、简单选择排序

--------------------------------------------------------------------------



n个元素进行简单选择排序,当排序第i个元素时,通过n-i次对元素间的比较,从n-i+1个元素中选出关键字最小的元素,并和第i个元素交换,当i=n时所有元素有序排列。  

![简单选择排序](https://img-blog.csdnimg.cn/99d7dec5fa4b479b87dddaf9675b315c.png)



/**

 * 简单选择排序

 * @param data

 */

public void selectSort(int[] data){

	int i, j, k;

	for(i=0;i<data.length-1;i++){

		k = i;//k存放最小元素下标

		for(j=i+1;j<data.length;j++){

			if(data[j]<data[k]){

				k = j;//记录当前最小元素的下标

			}

		}

		if(k != i){//存在比data[i]更小元素,交换data[i]与data[k]

			swap(data, i, k);

		}

	}

	System.out.println(Arrays.toString(data));

} 



[](https://gitee.com/vip204888/java-p7)4、希尔排序

------------------------------------------------------------------------



希尔排序是对直接插入排序方法的改进。  

其基本思想是:对待排序的n个元素,首先取d1(d1<n)作为分组间距,将所有距离为d1倍数的元素放在同一组中,由此将所有待排列元素分为d1组,在每个组内做直接插入排序;接着取d2(d2<d1)作为分组间距得到d2个组,组内进行直接插入排序…重复上述操作直到di=1,此时得到所有待排序元素分为一组且基本有序,进行直接插入排序可以得到最终有序排列的数组。  

![希尔排序](https://img-blog.csdnimg.cn/1994841df96849778de0ed1a9506b1aa.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3NpbmF0XzM3NjIzNDkw,size_16,color_FFFFFF,t_70)



/**

 * 希尔排序

 * @param data

 */

public void shellSort(int[] data){

	int i, j, k, s, tmp;

	k = data.length;

	int[] dist = new int[k/2];

	i = 0;

	do{

		k = k / 2;

		dist[i++] = k;

	}while(k > 1);//得到每次分组的间距,直到1为止

	

	for(i=0;(s = dist[i])>0;i++){//取分组间距

		System.out.println("分组间距:" + s + ",此次排序得到:");

		for(k=s;k<data.length;k++){//对每个分组内元素做直接插入排序

			if(data[k] < data[k-s]){

				tmp = data[k];

				for(j=k-s;j>=0&&data[j]>tmp;j-=s){

					data[j+s] = data[j];

				}

				data[j+s] = tmp;

			}

		}

		System.out.println(Arrays.toString(data));

	}

} 



[](https://gitee.com/vip204888/java-p7)5、快速排序

------------------------------------------------------------------------



快速排序的基本思想是:通过一趟排序将待排的元素划分为独立的两部分,成为前半区和后半区。前半区中的元素都不大于后半区中的元素。然后再分别对这两部分进行快速排序,进而使整个序列有序。



快速排序中一次划分的具体方法是:设待排序元素中的一个元素为枢轴元素pivot,另外设两个指针 i 和 j 分别指向序列的首尾位置。假设本次枢轴元素取i最初指向元素,则首先从j所指位置起向前搜索,找到第一个小于pivot的元素时,将该元素移到i所指的位置;然后从i所在位置开始向后搜索,找到第一个大于pivot的元素时将其移到j所指位置。重复该过程直到i等于j,将pivot复制到i和j指向位置。同理,若枢轴元素取j最初指向元素,则首先从i所指位置向后搜索。  

如下图所示,对数组进行一次划分得到前半区\[5,9,12,21\]和后半区\[23,76,32\],再分别对两个分区进行快速排序,即可得到有序序列\[5,9,12,21,23,32,76\]。  

![快速排序的一次划分](https://img-blog.csdnimg.cn/d29052aa7aca4b3b8e7626443fd21cad.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3NpbmF0XzM3NjIzNDkw,size_16,color_FFFFFF,t_70)  

快速排序被认为是在复杂度为O(nlogn)的排序方法中最好的一种,但是若初始序列元素有序或基本有序时,每次划分结果都会得到一个长度为0的分区,此时快速排序的性能退化为O(n²)。为了避免性能退化,一般会在选择枢轴元素时采用三数取中的方法。  

三数取中:即在待排序数组中对首、中、尾三个元素进行大小比较,选择中间大小的元素作为枢轴元素。  

![三数取中](https://img-blog.csdnimg.cn/5686fe86d7ac467ab1777b4e544440f3.png)



/**

 * 快速排序

 * @param data

 */

public void quickSort(int[] data, int low, int high){

	if(low < high){

		int loc = partition(data, low, high);//进行一次分区

		quickSort(data, low, loc - 1);//对前半区快速排序

		quickSort(data, loc + 1, high);//对后半区快速排序

	}

}



/**

 * 一次划分后,得到数组以枢轴元素data[i]为界,前半区元素都不大于data[i],后半区元素都不小于data[i]

 * @param data

 * @param low

 * @param high

 * @return

 */

public int partition(int[] data, int low, int high){

	getPivot(data, low, high);

	int pivot = data[high-1];//枢轴元素

	int i = low;

	int j = high - 1;

	while(i < j){

		while(i < j && data[++i] <= pivot){}

		data[j] = data[i];//data[i]大于pivot,移到后半区,此时原data[j]的值已保存在pivot或data[i]中

		while(i < j && data[--j] >= pivot){}

		data[i] = data[j];//data[j]小于pivot,移到前半区,此时原data[i]的值已保存在data[j]中

	}

	data[i] = pivot;

	return i;

}



/**

 * 取首、中、尾三者中间大小元素,避免快速排序性能退化

 * @param data

 * @param low

 * @param high

 */

public void getPivot(int[] data, int left, int right){

	int mid = (left + right) / 2;

	//对三个元素按递增排序

    if (data[left] > data[mid]) {

        swap(data, left, mid);

    }

    if (data[left] > data[right]) {

        swap(data, left, right);

    }

    if (data[right] < data[mid]) {

        swap(data, right, mid);

    }

    //将枢轴元素放置在待排序数组后

    swap(data, right-1, mid);

}



/**

 * 快速排序非递归

 * @param data

 */

public void quickSortNotRecur(int[] data, int low, int high){

	//使用栈来实现递归的压栈出栈操作

	Stack<Object> s = new Stack<Object>();

	s.push(low);

	s.push(high);

	if(!s.empty()){

		high = (Integer) s.pop();

		low = (Integer) s.pop();

		int loc = partition(data, low, high);//进行一次分区

		s.push(loc+1);//后半区先进后出

		s.push(high);

		s.push(low);//前半区后进先出

		s.push(loc-1);

	}

} 



[](https://gitee.com/vip204888/java-p7)6、堆排序

-----------------------------------------------------------------------



对待排列的n个元素组成的序列,将其看成一个完全二叉树,如果该二叉树中的任一非叶子节点的值均不小于(不大于)其左右子树节点,则该二叉树称为大根堆(小根堆),树的根节点(堆顶元素)是序列中最大(最小)的元素。  

堆排序的基本思想是:对待非递减排列的序列首先建立一个初始堆,此时输出堆顶元素,即最大值元素,然后取最后一个叶子节点替代堆顶元素,并将剩下的元素重新构建为大根堆,再输出新堆的堆顶元素作为次大值元素…以此类推,直到所有的元素有序排列。  

![堆排序](https://img-blog.csdnimg.cn/5abb79842dd9427e8832e59d43247cac.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3NpbmF0XzM3NjIzNDkw,size_16,color_FFFFFF,t_70)



/**

 * 堆排序

 * @param data

 */

public void heapSort(int[] data){

	int i, n, tmp;

	n = data.length;

	for(i=n/2-1;i>=0;i--){//从n位置子节点所在子树开始向上调整整个数组为大根堆(构建初始堆)

		heapAdjust(data, i, n-1);

	}

	for(i=n-1;i>0;i--){//从初始堆取堆顶(最大元素),对剩余元素重新构建大根堆

		tmp = data[0];//取堆顶

		data[0] = data[i];//将最末尾子节点交换到堆顶

		data[i] = tmp;//当前最大值放到数组末尾

		heapAdjust(data, 0, i-1);//剩余i-1个元素构建大根堆

	}

	System.out.println(Arrays.toString(data));

}



/**

 * 将待排序元素调整为大根堆

 * @param data

 * @param s

 * @param m

 */

public void heapAdjust(int[] data, int s, int m){

	int i, tmp;

	tmp = data[s];//备份当前父节点关键字

	for(i=2*s+1;i<=m;i=2*i+1){

		if(i<m && data[i]<data[i+1]){//找到子节点中关键字最大者

			i++;

		}

		if(tmp>=data[i]) break; //所有子节点都不大于父节点,本次无需调整

		data[s] = data[i]; //存在比父节点大的子节点,插入s位置

		s = i; //以i作为父节点的子树可能因为本次调整不再是大根堆,因此需要进行调整

	}

	data[s] = tmp;//将备份关键字插入最后做了交换的节点位置

} 



[](https://gitee.com/vip204888/java-p7)7、归并排序

------------------------------------------------------------------------



归并排序的一种实现方法是把一个有n个记录的无序文件看成是由n个长度为1的有序子文件组成的文件,对子文件两两归并,得到n/2个长度为2或1的有序文件,再两两归并,重复得到一个包含n个记录的有序文件。  

![归并排序](https://img-blog.csdnimg.cn/849a5694e2a8463f88f6cfd704ec2a8d.png)  

假设一次归并的两个文件为数组arr1和数组arr2,则需要一个大小为arr1和arr2长度之和的辅助空间arrTmp来进行归并,此时三个指针分别指向arr1,arr2和arrTmp初始位置,每次对比arr1和arr2指针指向元素,将其中较小值插入到arrTmp指针所指位置。当arr1,arr2中任一数组末尾元素插入到了arrTmp中,则将另一个数组剩余元素直接全部插入arrTmp中即可。如下图所示:  

![借助辅助数组进行归并](https://img-blog.csdnimg.cn/735654007c6141d9a331c0608d4564c3.png)



/**

 * 归并排序

 * @param data

 * @param s

 * @param t

 */

public void mergeSort(int[] data, int left, int right){

	if(left < right){

		int mid = (left + right) >> 1;

		mergeSort(data, left, mid);//前半区归并得到有序子序列data[left..mid]

		mergeSort(data, mid + 1, right);//后半区归并得到有序子序列data[mid..right]

		merge(data, left, mid, right);//data[left..mid]和data[mid..right]归并得到有序的data[s..t]

	}

}



/**

 * 对两个序列data[s..m]和data[m+1..n]进行归并

 * @param data

 * @param s

 * @param m

 * @param n

 */

public void merge(int[] data, int s, int m, int n){

	int i = s, j = m + 1, k = 0;//i指向data[m+1..n]初始位置,j指向data[s..m]初始位置,k指向tmp[]初始位置

	int st = s;

	int[] tmp = new int[data.length];

	while(i<=m&&j<=n){//归并data[s..m]和data[m+1..n]存入临时数组tmp[]

		if(data[i]<=data[j]){

			tmp[k++] = data[i++];

		}else{

			tmp[k++] = data[j++];

		}

	}

	for(;i<=m;i++){//将剩余data[s..m]元素复制到tmp[]

		tmp[k++] = data[i];

	}

	for(;j<=n;j++){//将剩余data[m+1..n]元素复制到tmp[]

		tmp[k++] = data[j];

	}

	for(i=0;i<k;i++){//将临时数组元素复制回原数组

		data[st++] = tmp[i];

	}

	//用于输出信息

	if(s == 0 && n == data.length-1 >> 1){

		System.out.println("前半区归并完成:" + Arrays.toString(data));

	}

	if(s == data.length >> 1 && n == data.length - 1){

		System.out.println("后半区归并完成:" + Arrays.toString(data));

	}

} 



[](https://gitee.com/vip204888/java-p7)8、基数排序

------------------------------------------------------------------------



基数排序是一种通过比较元素各个位数大小来进行排序的方法:基数r是按照元素数值的进制来确定的,比如十进制r=10,八进制则r=8。d原来表示元素的位数,d取所有待排序元素的位数最大值,比如125则d=3。  

进行基数排序时,设立r个队列,队列编号分别从0到r-1。首先按照最低有效位大小将元素分别排进队列中,然后按照队列中的顺序将元素重新排列。接着按照次低有效位将重新排列好的元素再次排进队列中…如此重复直到最高有效位完成排列,此时从队列取出的元素有序排列。  

![基数排序](https://img-blog.csdnimg.cn/f0eefaabf8e648eba0be16f8092bb304.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3NpbmF0XzM3NjIzNDkw,size_16,color_FFFFFF,t_70)


# 1200页Java架构面试专题及答案

小编整理不易,对这份**1200页Java架构面试专题及答案**感兴趣劳烦帮忙**转发/点赞一下,[然后点击这里即可免费领取!](https://gitee.com/vip204888/java-p7)**

![](https://img-blog.csdnimg.cn/img_convert/956d04130251c7383e12606838d7e6fa.png)

![](https://img-blog.csdnimg.cn/img_convert/b876b25de80a46f701f7471b953103a4.png)

# 百度、字节、美团等大厂常见面试题

进行基数排序时,设立r个队列,队列编号分别从0到r-1。首先按照最低有效位大小将元素分别排进队列中,然后按照队列中的顺序将元素重新排列。接着按照次低有效位将重新排列好的元素再次排进队列中…如此重复直到最高有效位完成排列,此时从队列取出的元素有序排列。  

![基数排序](https://img-blog.csdnimg.cn/f0eefaabf8e648eba0be16f8092bb304.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3NpbmF0XzM3NjIzNDkw,size_16,color_FFFFFF,t_70)


# 1200页Java架构面试专题及答案

小编整理不易,对这份**1200页Java架构面试专题及答案**感兴趣劳烦帮忙**转发/点赞一下,[然后点击这里即可免费领取!](https://gitee.com/vip204888/java-p7)**

[外链图片转存中...(img-7WeaMAW8-1628084245286)]

[外链图片转存中...(img-cpm5Wolx-1628084245288)]

# 百度、字节、美团等大厂常见面试题

![](https://img-blog.csdnimg.cn/img_convert/f7c028675fe2b2f249553361c8a93d6d.png)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值