一:冒泡排序
public class BubbleSort {
//冒泡排序:从位置0到n-1,位置0循环和后面的元素比较,比位置0小的就交换,一次循环下来位置0是最小的。
public int[] bubbleSortTwoMine(int[] nums){
// 2 , 5 , 3 , 7 length=4
// 0 1 2 3
int len = nums.length;
//小的放前面
for (int i=0;i<len-1;i++){
for (int j=i+1;j<len;j++){
if (nums[j]<nums[i]){
int tem = nums[i];
nums[i] = nums[j];
nums[j] = tem;
}
}
}
return nums;
}
public static void main(String[] args) {
int[] arr = new int[]{2,5,3,7};
BubbleSort b = new BubbleSort();
int[] resultArr = b.bubbleSortTwoMine(arr);
for (int i=0;i<resultArr.length;i++){
System.out.println(resultArr[i]);
}
}
}
二:选择排序
public class SelectionSort {
//选择排序:从位置0到n-1,位置0循环和后面的元素比较,比位置0小的先不交换,而是记录minIndex和minValue,一次循环下来再交换位置0和最小元素的下标minIndex。
//相当于一次循环选择最小的元素下标
public int[] selectSort(int[] arr) {
// 2 5 3 7 length=4
// 0 1 2 3
int len = arr.length;
for (int i=0;i<len-1;i++){
int minIndex = i;
int minValue = arr[i];
for (int j=i+1;j<len;j++){
if (arr[j]<minValue) {
minIndex = j;
minValue = arr[j];
}
}
int tmp = arr[i];
arr[i] = arr[minIndex];
arr[minIndex] = tmp;
}
return arr;
}
public static void main(String[] args){
SelectionSort s = new SelectionSort();
int[] arr = new int[]{2,5,3,7,15,12,11,19,16};
int[] resultArr = s.selectSort(arr);
for (int i=0;i<resultArr.length;i++){
System.out.println(resultArr[i]);
}
}
}
三:插入排序
public class InsertSort {
//插入排序:从位置1开始,把1前面的当作排好序的,位置1及后面的为没有排序的,循环没有排序的,一个一个插入到排序好的里面。
public int[] insertSort(int[] arr){
// 2 5 3 7 length=4
// 0 1 2 3
// i
//下标i=1开始的是未排序的序列
//下标i前的是排序好的序列
int len = arr.length;
for (int i=1;i<len;i++) {//2
int j = i;
while (j>0&&arr[j]<arr[j-1]){
int tmp = arr[j];
arr[j] = arr[j-1];
arr[j-1] = tmp;
j--;
}
}
return arr;
}
public static void main(String[] args) {
InsertSort in = new InsertSort();
int[] arr = new int[]{2,5,1,7,15,16,8,3};
int[] resultArr = in.insertSort(arr);
for (int i=0;i<resultArr.length;i++){
System.out.println(resultArr[i]);
}
}
}
四:希尔排序
//希尔排序:多个步长,每个步长分对应的子序列。每个步长中的子序列进行插入排序
public class ShellSort {
public int[] shellSort(int[] arr){
//3 1 5 7 2 length=5
//0 1 2 3 4 5/2=2 2/2 =1 1/2=0
// gap
// j
int len = arr.length;
for (int gap=len/2;gap>0;gap=gap/2){//多个步长,每个步长分对应的子序列。每个步长中的子序列进行插入排序
for (int i=gap;i<len;i++){
int j = i;
//此处循环条件不能是j>0,因为j是大于0了,但是j-gap可能不大于等于0,就会下标越界。即条件应为j-gap>=0
while (j-gap>=0&&arr[j]<arr[j-gap]){
swap(arr,j,j-gap);
j= j-gap;
}
}
}
return arr;
}
public void swap(int[] arr,int m,int n){
int tmp = arr[m];
arr[m] = arr[n];
arr[n] = tmp;
}
public static void main(String[] args) {
int[] arr = new int[]{2,5,1,7,15,16,8,3,3};
//int[] arr = new int[]{3,1,5,7,2};
ShellSort sh = new ShellSort();
int[] resultArr = sh.shellSort(arr);
for (int i=0;i<resultArr.length;i++){
System.out.println(resultArr[i]);
}
}
}
五:归并排序
//归并排序:排序当前数组,要先排序左半边子数组和右半边数组
//当left=right,两个子数组都只有一个必定有序,直接返回排序好的数组。
//当前方法中,将获取到两个排序的子数组,有序的放到一个当前需要返回的长度数组中,最后返回
public class MergeSort {
public int[] mergeSort(int[] arr) {
// left right
// 5 1 3 7 length=4
// 0 1 2 3
int[] returnArr = recursion(arr,0,arr.length-1);
return returnArr;
}
public int[] recursion(int[] arr,int left,int right){
int len = right-left+1;
int[] resultArr = new int[len];
if (left==right) {
resultArr[0] = arr[left];
return resultArr;
}
//这个是关键:当右边的子数组是两个元素时,如果用len/2+1,那位置就错乱了
int mid = (right-left)/2 + left+1;
int[] leftArr = recursion(arr,left,mid-1); //排好序的 1 5
int[] rightArr = recursion(arr,mid,right); //排好序的 3 7
int l_len = leftArr.length;
int r_len = rightArr.length;
int m = 0;
int n = 0;
int k = 0;
while (m<l_len || n<r_len){
if (m>=l_len){
while (n<r_len){
resultArr[k] = rightArr[n];
n++;
k++;
}
}else if(n>=r_len){
while (m<l_len){
resultArr[k] = leftArr[m];
m++;
k++;
}
}else {
resultArr[k] = leftArr[m]<rightArr[n] ? leftArr[m++] : rightArr[n++];
k++;
}
}
return resultArr;
}
public static void main(String[] args) {
int[] arr = new int[]{2, 5, 1, 7, 15, 16, 8, 3, 3};
//int[] arr = new int[]{5,1,3,7};
//Solution so = new Solution();
//int[] resultArr = so.mergeSort(arr);
MergeSort me = new MergeSort();
int[] resultArr = me.mergeSort(arr);
for (int i = 0; i < resultArr.length; i++) {
System.out.println(resultArr[i]);
}
}
}
六:快速排序
//快排的两种思想交换、挖坑: https://blog.youkuaiyun.com/m0_62171658/article/details/124313424
public class QuickSort {
//快速排序:以第一个元素作为基准,把数组分成两半,左边的都比基准元素小,右边的都比基准元素大。然后递归调这个分两半的函数
public int[] quickSort(int[] arr) {
// 2 5 3 1 7 length=5
// 0 1 2 3 4
// l r
// 2 1 3 5 7
// lr
// 1 2 3 5 7
recursion(arr, 0, arr.length - 1);
return arr;
}
public void recursion(int[] arr, int left, int right) {
if (left >= right) return;
//基准是第一个元素,left位置的
//l初始指向left边界:因为当数组是 2,1时,l如果初始指向left+1,下面的循环条件l不会++,总的循环结束之后要交换两个的位置
int l = left + 1;
int r = right;
while (l <= r) {
//从left开始一直找,直到找到比基准位置x的元素大的位置
while (arr[l] < arr[left]) {
l++;
if (l > right) break;
}
//从right开始一直找,直到找到比基准位置x的元素小的位置
while (arr[r] >= arr[left]) {
r--;
if (r < 0) break;
}
//交换l和r的位置
if (l < r) swap(arr, l, r);
}
//循环结束:l>=r,此时l的位置的元素比位置left大,所以位置left要交换位置l-1的,就可以把整个数组以基准位置的值分左右两半,左:left->l-2 右:l->right
swap(arr, l - 1, left);
recursion(arr, left, l - 2);
recursion(arr, l, right);
}
public void swap(int[] arr, int m, int n) {
int tmp = arr[m];
arr[m] = arr[n];
arr[n] = tmp;
}
public static void main(String[] args) {
int[] arr = new int[]{2, 1, 17, 31, 17, 17, 17};
SolutionAscMine sm = new SolutionAscMine();
int[] resultArr = sm.quickSort(arr);
for (int i = 0; i < resultArr.length; i++) {
System.out.println(resultArr[i]);
}
}
}
七:堆排序
得益于完全二叉树的结构,下标为index的节点,
它的父节点下标是 (index - 1) / 2,
左子节点下标是 2 * index + 1,
右子节点下标是 2 * index + 2
//堆排"完全二叉树中的位置”:https://blog.youkuaiyun.com/yldmkx/article/details/109669261
//堆排"完全二叉树中的位置”:节点下标m,父节点下标为 (m-1)/2 ; 父节点下标为m,左孩子下标为 2m+1 ,右孩子下标为2m+2
//堆排就是从树的最下层最右边节点的父节点开始,构建堆,构建时是递归的。循环下一个每个父节点构建堆
public class HeapSort {
public int[] heapSort(int[] arr){
// 3 5 7 4 2 length=5
// 0 1 2 3 4
// 3(0)
// 5(1) 7(2)
// 4(3) 2(4)
int len = arr.length;
int firstParent = (len-1-1)/2;
for (int i=firstParent;i>=0;i--){
recursion(i,arr,len);
}
for (int i=len-1;i>0;i--){
swap(arr,0,i);
recursion(0,arr,i);
}
return arr;
}
//len决定"完全二叉树最小层,最右边节点的位置"
//adj是需要调整的父节点的下标
//最大堆
public void recursion(int adj,int[] arr,int len){
if (adj*2+1>=len) return;
int left = adj*2+1;
int max = left;
int right = adj*2+2;
if (right<len && arr[right] > arr[left]){
max = right;
}
if (arr[max]>arr[adj]) {
swap(arr,max,adj);
recursion(max,arr,len);
}
}
public void swap(int[] arr, int m,int n){
int tmp = arr[m];
arr[m] = arr[n];
arr[n] = tmp;
}
public static void main(String[] args) {
HeapSort heap = new HeapSort();
//int[] arr = new int[]{3,5,7,4,2};
int[] arr = new int[]{2, 1, 17, 31, 17, 17, 17};
int[] resultArr = heap.heapSort(arr);
for (int i=0;i<resultArr.length;i++){
System.out.println(resultArr[i]);
}
}
}
八:计数排序
//计数排序:计算需要排序的数组中的最大值和最小值,新建一个可以存下这个范围内所有数值的数组,用来计数需要排序数组中的值出现的次数。
//循环计数数组,下标+min就是需要排序数组中的元素原原来的值,加入到返回数组中。
public class CountingSort {
public int[] countingSort(int[] arr){
// 3 1 5 length = 3
// 0 1 2 min:1 max:5 新的数组的长度len=max-min+1; len=5
int min = arr[0];
int max = arr[0];
for (int i=0;i<arr.length;i++){
int tmp = arr[i];
min = tmp < min ? tmp :min;
max = tmp >max ? tmp :max;
}
int len = max-min+1;
int[] countArr = new int[len];//下标从0开始,即值为5下标就是5-min,所以对应下标的值就是:下标+min
for (int j=0;j<arr.length;j++){ //循环所有的数组,根据值,放到countArr对应的下标中,重复的就+1
int value = arr[j];
int index = value-min;
countArr[index] +=1;
}
int[] resultArr = new int[arr.length];
int m = 0;
for (int k=0;k<countArr.length;k++){//循环计数数组,根据下标计算值,计数数组中下标对应的值>1说明原数组中出现两次值,循环放入返回数组中
while (countArr[k]>0){
resultArr[m] = k+min;
m++;
countArr[k] -=1;
}
}
return resultArr;
}
public static void main(String[] args) {
int[] arr = new int[]{2, 5, 1, 7, 15, 16, 8, 3, 3};
//int[] arr = new int[]{5,1,3,7};
CountingSort co = new CountingSort();
int[] resultArr = co.countingSort(arr);
for (int i = 0; i < resultArr.length; i++) {
System.out.println(resultArr[i]);
}
}
}
九:桶排序
//桶排序:根据需要排序的数组中的最大值和最小值除以桶数+1,决定这个桶存的哪个范围值的元素。
// 二维数组的第一个下标是具体的哪个桶,桶是一个一维数组,存在这个范围的元素,然后对这个桶进行插入排序。最后循环所有桶,循环桶里面的元素加到返回数组中。
//数组在方法中是值传递,传的相当于是副本,有点奇怪
public class BucketSort {
public int[] bucketSort(int[] arr){
// 5 1 3 7 9 12 15 length=7 minValue=1 maxValue = 15
// 0 1 2 3 4 5 6
//定义桶个数为5
int bucket = 5;
int[][] bucketArr = new int[bucket][0];//每次插入到具体的桶要扩容桶的大小
//获取数组中的最大值和最小值
int minValue = arr[0];
int maxValue = arr[0];
for (int i=0;i<arr.length;i++){
minValue = arr[i]<minValue ? arr[i] : minValue;
maxValue = arr[i]>maxValue ? arr[i] : maxValue;
}
//每个桶的值的范围: 1-3,4-6,7-9,10-12,13-15
// 0 1 2 3 4
int range = (maxValue-minValue)/bucket+1; //3
//可以根据数组元素值确定哪个桶: x-minValue /range
for (int j=0;j<arr.length;j++){
int ele = arr[j];
int index = (ele-minValue)/ range;
//数组在方法中是值传递,传的相当于是副本,有点奇怪。
//加入到对应的桶中,并返回扩容后的数组
bucketArr[index] = addBucket(bucketArr[index],ele);
}
int[] resultArr = new int[arr.length];
int result_index = 0;
//对每个桶进行插入排序
for (int m=0;m<bucketArr.length;m++){
int[] insertArr = bucketArr[m];
// 1 3 length=2
// 0 1
//因为
for (int n=1;n<insertArr.length;n++){
int k=n;
while (k>0&&insertArr[k]<insertArr[k-1]){
swap(insertArr,k,k-1);
k--;
}
}
//对一个桶插入排序好后,将insertArr中的数据循环放入返回数组中
for (int p=0;p<insertArr.length;p++){
resultArr[result_index] = insertArr[p];
result_index++;
}
}
return resultArr;
}
//给桶中插入元素并扩容,并返回新的数组
public int[] addBucket(int[] array,int value){
int len = array.length;
int[] tmpArr = new int[len+1];
int k=0;
for (int i=0;i<array.length;i++){
tmpArr[k] = array[i];
k++;
}
tmpArr[len]= value;
//将扩容的数组赋值给array
return tmpArr;
}
public void swap(int[] arr,int m,int n){
int tmp = arr[m];
arr[m] = arr[n];
arr[n] = tmp;
}
public static void main(String[] args) {
int[] arr = new int[]{5,1,3,7,9,12,15};
//int[] arr = new int[]{5,1,3,7};
BucketSort bu = new BucketSort();
int[] resultArr = bu.bucketSort(arr);
for (int i = 0; i < resultArr.length; i++) {
System.out.println(resultArr[i]);
}
System.out.println("---------");
int[] arr1 = new int[]{1,2,3};
int[] arr2 = new int[]{4,5,6};
arr1 = arr2;
System.out.println(arr1);
//二维数组第一个下标对应的是一个一维数组 :书里的一页
//三维数组的第一个下标对应的是一个二维数组 :一本书
//四维数组的第一个下标对应的是三维数组: 书架
//五维数组的第一个下标对应的是四维数组: 图书馆
int[][] twoArr = new int[][]{{1,2},{7,8}};
twoArr[0] = arr2;
System.out.println(twoArr);
}
}
十:基数排序
//基数排序:计算需要排序的数组中最大的值,得到最大的位数 ,123最大位数是3。三层循环根据个、十、百排序
// 考虑到有负数,所以需要20个桶装不同位的基数的元素,循环个、十、百 。分别排序放到放到对应的基数桶中,然后循环桶,放到返回数组(即原数组)中。
// 个、十、百每层的桶要是新桶,切循环的数据是上一位拍排序好的数组。
//最后百位排序好的就是有序数组
public class RadixSort {
// 5 11 3 123 1 length = 5
// 0 1 2 3 4
public int[] radixSort(int[] arr){
int maxValue = arr[0];
for(int i=0;i<arr.length;i++){
maxValue = arr[i] > maxValue ? arr[i] : maxValue;
}
//计算最大值的位数 123 12 1 0
int k = 0; // 最大位数为3
while (maxValue>0){
k++;
maxValue /=10;
}
//根据个、十、百位,放入长度为20的桶中,下标0-9为负数,10-19为整数
// -9 -8 -7 -6 -5 -4 -3 -2 -1 0 1 2 3 4 5 6 7 8 9
// 0 1 2 3 4 5 6 7 8 9 10 11
int step = 9;
int divisor = 1;
for (int j=0;j<k;j++){
int poi = 0;
//桶,不同基数,桶要清空,要不然会有上一次桶中的数据
int[][] bucketArr = new int[20][0];
for (int m=0;m<arr.length;m++){
int radix = (arr[m]/divisor)%10;//如果个位是1,则放入下标10的桶中
int index = radix+step;
//扩对应桶的长度,因为刚开始是0;
bucketArr[index] = addBucket(bucketArr[index],arr[m]);
}
divisor *=10;
for (int n=0;n<bucketArr.length;n++){
int[] tmpArr = bucketArr[n];
for (int y=0;y<tmpArr.length;y++){
arr[poi] = tmpArr[y];
poi++;
}
}
}
return arr;
}
public int[] addBucket(int[] arr,int value){
int len = arr.length;
int[] resultArr = new int[len+1];
int k=0;
for (int i=0;i<arr.length;i++){
resultArr[k] = arr[i];
k++;
}
resultArr[len] = value;
return resultArr;
}
public static void main(String[] args) {
//int[] arr = new int[]{5,1,3,7,9,12,15};
int[] arr = new int[]{5,1,3,17,-1};
RadixSort ra = new RadixSort();
int[] resultArr = ra.radixSort(arr);
for (int i = 0; i < resultArr.length; i++) {
System.out.println(resultArr[i]);
}
}
}