冒泡排序
排序(假设从小到大)的步骤如下:
package com.ty;
public class BubbleSort {
public static void main(String[] args) {
int[] arr= {1,10,5,6,3,8,2,0};
bubbleSort(arr);
printArr(arr);
}
public static void bubbleSort(int[] arr){
int n=arr.length;
for(int i=n-1;i>=0;i--){
for(int j=0;j<i;j++){
if(arr[j]>arr[j+1]){
swap(arr,j,j+1);
}
}
}
}
public static void swap(int[] arr,int i,int j){
int temp;
temp=arr[i];
arr[i]=arr[j];
arr[j]=temp;
}
public static void printArr(int[] arr){
for(int i:arr){
System.out.print(i+" ");
}
}
}
选择排序
排序的步骤如下:
package com.ty;
/**
* @description: 选择排序
*/
public class SelectSort {
public static void main(String[] args) {
int[] arr={1,10,5,6,3,8,2,0};
printArr(arr);
System.out.println();
selectSort(arr);
printArr(arr);
}
public static void selectSort(int[] arr){
int n=arr.length;
for(int i=0;i<n;i++){
int minValueIndex=i;
for(int j=i;j<n;j++){
minValueIndex=arr[minValueIndex]<arr[j]?minValueIndex:j;
}
swap(arr,minValueIndex,i);
}
}
public static void swap(int[] arr,int i,int j){
int temp;
temp=arr[i];
arr[i]=arr[j];
arr[j]=temp;
}
public static void printArr(int[] arr){
for(int i:arr){
System.out.print(i+" ");
}
}
}
插入排序
选择排序是每次选择出最小的放到已经排好的数组后面,而插入排序是依次选择一个元素,插入到前面已经排好序的数组中间,确保它处于正确的位置,当然,这是需要已经排好的顺序数组不断移动。步骤描述如下:
步骤描述如下:
package com.ty;
public class InsertSort {
public static void main(String[] args) {
int[] arr={1,7,5,4,3,2,9};
printArr(arr);
System.out.println();
insetSort(arr);
printArr(arr);
}
public static void insetSort(int[] arr){
int n=arr.length;
for(int end=1;end<n-1;end++){
for(int pre=end-1;pre>=0&&arr[pre]>arr[pre+1];pre--){
swap(arr,pre,pre+1);
}
}
}
public static void swap(int[] arr,int i,int j){
int temp;
temp=arr[i];
arr[i]=arr[j];
arr[j]=temp;
}
public static void printArr(int[] arr){
for(int i:arr){
System.out.print(i+" ");
}
}
}
希尔排序
希尔排序(Shell’s Sort)又称“缩小增量排序”(Diminishing Increment Sort),是插入排序的一种更高效的改进版本,同时该算法是首次冲破 O(n^2n 2 ) 的算法之一
希尔排序基本步骤如下:
public class ShellSort {
public static void shellSort(int[] nums) {
int times = 1;
for (int gap = nums.length / 2; gap > 0; gap /= 2) {
System.out.print(
"第" + (times++) + "轮希尔排序, gap= " + gap + " ,结果:"
);
for (int i = gap; i < nums.length; i++) {
int j = i;
int temp = nums[j];
while (j - gap >= 0 && temp < nums[j - gap]) {
// 移动法
nums[j] = nums[j - gap];
j -= gap;
}
nums[j] = temp;
}
printf(nums);
}
}
public static void printf(int[] nums) {
for (int num : nums) {
System.out.print(num + " ");
}
System.out.println("");
}
}
快速排序
快速排序比较有趣,选择数组的一个数作为基准数,一趟排序,将数组分割成为两部分,一部分均小于/等于基准数,另外一部分大于/等于基准数。然后分别对基准数的左右两部分继续排序,直到数组有序。这体现了分而治之的思想,其中还应用到挖坑填数的策略。
package com.ty;
public class QuickSort {
public static void main(String[] args) {
int[] arr={101,109,1,10,5,6,3,8,2,0,-1,99};
printArr(arr);
System.out.println();
quickSort(arr,0,arr.length-1);
printArr(arr);
}
public static void quickSort(int[] arr,int left, int right){
int pivot =arr[(left+right)/2];
int l=left,r=right;
// 让值比pivot 小的放在左边,比pivot 大的放在右边
while(l<r){
while (arr[l]<pivot){
l++;
}
while (arr[r]>pivot){
r--;
}
//此时pivot 左边的数字都比他小,右边的数字都比他大
if(l>=r){
break;
}
//进行交换
swap(arr,l,r);
//如果两个数交换的其中有一个的等于pivot
if(arr[l]==pivot){
r--;
}
if(arr[r]==pivot){
l++;
}
}
if(l==r){
l++;
r--;
}
//对排序好的左边部分进行递归
if(left<r){
quickSort(arr,left,r);
}
//对排序好的右边部分进行递归
if(l<right){
quickSort(arr,l,right);
}
}
public static void swap(int[] arr,int i,int j){
int temp;
temp=arr[i];
arr[i]=arr[j];
arr[j]=temp;
}
public static void printArr(int[] arr){
for(int i:arr){
System.out.print(i+" ");
}
}
}
身份证排序
安全局搜索到了一批 (n 个) 身份证号码,希望按出生日期对它们进行从大到小排序,如果有相同日期,则按身份证号码大小进行排序。身份证号码为 18 位的数字组成,出生日期为第 7 到第 14 位。
package com.ty.test01;
import java.io.*;
import java.util.ArrayList;
import java.util.List;
public class CartId {
public static void main(String[] args) {
BufferedReader bufferedReader= new BufferedReader(new InputStreamReader(System.in));
PrintWriter printWriter=new PrintWriter(new OutputStreamWriter(System.out));
List<String> strings=new ArrayList<>();
try(bufferedReader;printWriter) {
int n = Integer.parseInt( bufferedReader.readLine());
for (int i = 0; i < n; i++) {
strings.add(bufferedReader.readLine());
}
strings.sort((str1, str2) -> {
String sub1 = str1.substring(6, 14);
String sub2 = str2.substring(6, 14);
if (!sub1.equals(sub2)) {
return sub2.compareTo(sub1);
}
return str2.compareTo(str1);
});
for (String str : strings) {
printWriter.println(str);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
5
466272307503271156
215856472207097978
234804580401078365
404475727700034980
710351408803093165
404475727700034980
234804580401078365
215856472207097978
710351408803093165
466272307503271156
归并排序
前面学的快速排序,体现了分治的思想,但是不够典型,而归并排序则是非常典型的分治策略。归并的总体思想是先将数组分割,再分割 … 分割到一个元素,然后再两两归并排序,做到局部有序,不断地归并,直到数组又被全部合起来。
package com.ty;
public class MergeSort {
public static void merge(int[]arr,int left,int mid,int right){
int[]temp=new int[right-left+1];
int l=left;
int r=mid+1;
int t=0;
// 比较左右两部分的元素,哪个小,就把那个元素填入temp中
while(l<=mid&&r<=right){
if(arr[l]<=arr[r]){
temp[t]=arr[l];
t++;
l++;
}else {
temp[t] = arr[r];
t++;
r++;
}
}
// 如果左边还有元素剩下,则全部合并过去
while (l<=mid){
temp[t]=arr[l];
t++;
l++;
}
// 如果右边有元素剩下,则把右边元素合并过去
while (r<=right){
temp[t]=arr[r];
t++;
r++;
}
//把最后的排序结果复制到原数组
for(int i=0;i<temp.length;i++){
arr[left+i]=temp[i];
}
}
public static void sort(int[]arr,int left,int right){
if(left==right){
return;
}
int mid=(left+right)/2;
sort(arr,left,mid);
sort(arr,mid+1,right);
merge(arr,left,mid,right);
}
public static void printArr(int[] arr){
for(int i:arr){
System.out.print(i+" ");
}
}
public static void main(String[] args) {
int[] arr={101,109,1,10,5,6,3,8,2,0,-1,99};
printArr(arr);
System.out.println();
sort(arr,0,arr.length-1);
printArr(arr);
}
}
计数排序
计数排序,不是基于比较,而是基于计数,比较适合元素数值相近且为整数的情况。
首先先遍历一遍,找出最小的 min 和最大的元素 max,创建一个大小为 max - min + 1 的数组,再遍历一次,统计数字个数,写到数组中。
然后再遍历一次统计数组,将每个元素置为前面一个元素加上自身,为什么这样做呢?
这是为了让统计数组存储的元素值等于相应整数的最终排序位置,这样我们就可以做到稳定排序,比如下面的 15 对应的是 8,也就是 15 在数组中出现是第 8 个元素,从后面开始遍历,我们就可以保持稳定性。
public class CountSort {
public static void countSort(int[] nums) {
int max = nums[0];
int min = nums[0];
for (int i = 1; i < nums.length; i++) {
if (nums[i] > max) {
max = nums[i];
}
if (nums[i] < min) {
min = nums[i];
}
}
System.out.println("min:" + min + ",max:" + max);
int count = max - min;
int[] countNums = new int[count + 1];
for (int i = 0; i < nums.length; i++) {
countNums[nums[i] - min]++;
}
System.out.print("countNums: ");
printf(countNums);
int sum = 0;
// 后面的元素等于前面元素加上自身
for (int i = 0; i < count + 1; i++) {
sum += countNums[i];
countNums[i] = sum;
}
System.out.print("countNums: ");
printf(countNums);
int[] newNums = new int[nums.length];
for (int i = nums.length - 1; i >= 0; i--) {
/**
* nums[i] - min 表示原数组 nums 里面第i位置对应的数在统计数组里面的位置索引
*/
newNums[countNums[nums[i] - min] - 1] = nums[i];
countNums[nums[i] - min]--;
}
printf(newNums);
}
public static void printf(int[] nums) {
for (int num : nums) {
System.out.print(num + " ");
}
System.out.println("");
}
}
桶排序
桶排序,是指用多个桶存储元素,每个桶有一个存储范围,先将元素按照范围放到各个桶中,每个桶中是一个子数组,然后再对每个子数组进行排序,最后合并子数组,成为最终有序的数组。这其实和计数排序很相似,只不过计数排序每个桶只有一个元素,而且桶存储的值为该元素出现的次数。
桶排序的具体步骤:
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class BucketSort {
public static void bucketSort(int[] nums) {
// 遍历数组获取最大最小值
int max = Integer.MIN_VALUE;
int min = Integer.MAX_VALUE;
for (int i = 0; i < nums.length; i++) {
max = Math.max(max, nums[i]);
min = Math.min(min, nums[i]);
}
// 计算桶的数量
int bucketNum = (max - min) / nums.length + 1;
System.out.println(
"最小:" + min + ",最大:" + max + ",桶的数量:" + bucketNum
);
List<List<Integer>> buckets = new ArrayList<List<Integer>>(bucketNum);
for (int i = 0; i < bucketNum; i++) {
buckets.add(new ArrayList<Integer>());
}
// 将每个元素放入桶
for (int i = 0; i < nums.length; i++) {
int num = (nums[i] - min) / (nums.length);
buckets.get(num).add(nums[i]);
}
// 对每个桶内部进行排序
for (int i = 0; i < buckets.size(); i++) {
Collections.sort(buckets.get(i));
}
// 将桶的元素复制到数组中
int index = 0;
for (int i = 0; i < buckets.size(); i++) {
for (int j = 0; j < buckets.get(i).size(); j++) {
nums[index++] = buckets.get(i).get(j);
}
}
}
public static void printf(int[] nums) {
for (int num : nums) {
System.out.print(num + " ");
}
System.out.println("");
}
}
堆排序
排序的思路为:
package com.ty;
public class HeapSort {
public static void heaptrify(int[] arr,int cur,int length){
int leftNode=cur*2+1;
int rightNode=cur*2+2;
int maxNode=cur;
if(leftNode<length&&arr[maxNode]<arr[leftNode]){
maxNode=leftNode;
}
if(rightNode<length&&arr[maxNode]<arr[rightNode]){
maxNode=rightNode;
}
if(maxNode!=cur){
swap(arr,maxNode,cur);
heaptrify(arr,maxNode,length);
}
}
public static void buildHeap(int[]arr){
int lastNode=arr.length-1;
int curNode=(lastNode-1)/2;
for (int i = curNode; i >=0 ; i--) {
heaptrify(arr,i, arr.length);
}
}
public static void heapSort(int[]arr){
buildHeap(arr);
int k=arr.length-1;
while(k>=0){
swap(arr,0,k);
heaptrify(arr,0,k);
k--;
}
}
public static void swap(int[] arr,int i,int j){
int temp;
temp=arr[i];
arr[i]=arr[j];
arr[j]=temp;
}
public static void printArr(int[] arr){
for(int i:arr){
System.out.print(i+" ");
}
}
public static void main(String[] args) {
int[] arr= {1,10,5,6,3,8,2,0,100,45};
printArr(arr);
heapSort(arr);
System.out.println();
printArr(arr);
}
}
基数排序
假设有一串数字,12,12, 10, 45, 32, 56, 677, 93, 22, 22, 30。
先准备一个盒子,里面有0到9的数据。
第一步、根据个位的数字将按照顺序排列到盒子里:10, 30, 12, 12, 32, 22, 22, 93, 45, 56, 677
第二步、根据十位的数字将按照顺序排列到盒子里:10, 12, 12, 22, 22, 30, 32, 45, 56, 677, 93
第三步、根据百位的的数字按照顺序排列到盒子里:10, 12, 12, 22, 22, 30, 32, 45, 56, 93, 677
1、判断这一串数字中最大数的位数。
2、因为每次排序只有位数不一样,所以排序的代码基本相同,用一个循环实现排序。
3、打印排序好的数。
import java.util.Arrays;
public class Paix {
public static void main(String[] args) {
int[] arr = new int[]{12,12, 10, 45, 32, 56, 677, 93, 22, 22, 30}; //定义一个数组
int max = 0;
for (int k : arr) { //得到这些数里面最大数的长度
if ((k + "").length() > max) {
max = (k + "").length();
}
}
for (int x = 0; x < max; x++) {
int[][] temp = new int[10][arr.length];
int[] tempcount = new int[10];
int n = (int) Math.pow(10, x);
for (int k : arr) {
int number = (((k / n) % 10));
temp[number][tempcount[number]] = k; //temp[number]默认值为0
tempcount[number]++;
}
int result = 0;
for (int i = 0; i < tempcount.length; i++) {
for (int j = 0; j < tempcount[i]; j++) {
arr[result] = temp[i][j];
result++;
}
}
System.out.println(Arrays.toString(arr));
}
}
}