Java->排序

目录

一、排序

1.概念

2.常见的排序算法

二、常见排序算法的实现 

1.插入排序

1.1直接插入排序

1.2希尔排序(缩小增量法)

1.3直接插入排序和希尔排序的耗时比较 

2.选择排序

2.1直接选择排序

2.2堆排序

2.3直接选择排序与堆排序的耗时比较

3.交换排序

3.1冒泡排序

3.2快速排序

1. Hoare版

2. 挖坑法

3. 前后指针

3.2.1快速排序的优化

1.三数取中法选key

2. 递归到小的子区间时,可以考虑使用插入排序

3.2.2非递归的快速排序

4.归并排序

1.归并排序 

2.非递归的归并排序 

3.海量数据的排序问题

5.排序算法复杂度及稳定性分析

​编辑

三、其他非基于比较排序

1.计数排序

2.基数排序

3.桶排序

4.比较

5.排序

一、排序

1.概念

所谓排序,就是使一串记录,按照其中的某个或某些关键字的大小,递增或递减的排列起来的操作。

稳定性:假定在待排序的记录序列中,存在多个具有相同的关键字的记录,若经过排序,这些记录的相对次序保持不变,即在原序列中,r[i]=r[j],且r[i]在r[j]之前,而在排序后的序列中,r[i]仍在r[j]之前,则称这种排序算法是稳定的;否则称为不稳定的。

 内部排序:数据元素全部放在内存中的排序

外部排序:数据元素太多不能同时放在内存中,根据排序过程的要求在内外存之间移动数据的排序

2.常见的排序算法

二、常见排序算法的实现 

1.插入排序

把待排序的记录按其关键码值的大小逐个插入到一个已经排好序的有序序列中,直到所有的记录插入完为止,得到一个新的有序序列

1.1直接插入排序

    public static void insertSort(int[] array) {
        for (int i = 1; i < array.length; i++) {
            int j = i - 1;
            int tmp = array[i];
            for (; j >= 0; j--) {
                if(array[j] > tmp) {
//if(arr[j] >= tmp)
//变为不稳定的排序
                    array[j+1] = array[j];
                }else {
//                    array[j+1] = tmp;
                    break;
                }
            }
            array[j+1] = tmp;
        }
    }

直接插入排序的特性总结:
1. 元素集合越接近有序,直接插入排序算法的时间效率越高
2. 时间复杂度:O(N^2)
3. 空间复杂度:O(1),它是一种稳定的排序算法
4. 稳定性:稳定

如果一个排序     本身就是稳定的排序        那么它就可以被实现为不稳定的排序,

但是一个排序     本身就是不稳定的排序    那么它不可能被实现为稳定的排序

1.2希尔排序(缩小增量法)

先选定一个整数,把待排序文件分为整数个组,再进行每个组内的排序

跳跃式分组: 

    public static void shellSort(int[] array) {
        int gap = array.length;
        while(gap > 1) {
            gap /= 2;
            shell(array,gap);
        }
    }

    private static void shell(int[] array, int gap) {
        for (int i = gap; i < array.length; i++) {
            int j = i - gap;
            int tmp = array[i];
            for (; j >= 0; j -= gap) {
                if(array[j] > tmp) {
                    array[j+gap] = array[j];
                }else {
                    break;
                }
            }
            array[j+gap] = tmp;
        }
    }

 希尔排序的特性总结:
1. 希尔排序是对直接插入排序的优化。
2. 当gap > 1时都是预排序,目的是让数组更接近于有序。当gap == 1时,数组已经接近有序的了,这样就会很快。这样整体而言,可以达到优化的效果。
3. 希尔排序的时间复杂度不固定:O(N^1.3)....O(N^1.5)
4. 空间复杂度:O(1)  ,不稳定

1.3直接插入排序和希尔排序的耗时比较 

import java.util.Arrays;
import java.util.Random;

public class Text {
    public static void readyDataOrder(int[] array) {
        Random random = new Random();
        for (int i = 0; i < array.length; i++) {
            array[i] = random.nextInt(100000);
        }
    }

    public static void InsertOrder(int[] array) {
        array = Arrays.copyOf(array,array.length);
        long start = System.currentTimeMillis();
        Sort.insertSort(array);
        long end = System.currentTimeMillis();
        System.out.println("插入排序耗时参考:" + (end - start));
    }

    public static void ShellOrder(int[] array) {
        long start = System.currentTimeMillis();
        Sort.shellSort(array);
        long end = System.currentTimeMillis();
        System.out.println("希尔排序耗时参考:" + (end - start));
    }

    public static void main(String[] args) {
        int[] array = new int[100000];
        readyDataOrder(array);

        InsertOrder(array);

        ShellOrder(array);
    }
}
//插入排序耗时参考:1884
//希尔排序耗时参考:22

2.选择排序

每一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,直到全部待排序的数据元素排完

2.1直接选择排序

    public static void selectSort(int[] array) {
        for (int i = 0; i < array.length; i++) {
            int minIndex = i;
            for (int j = i + 1; j < array.length; j++) {
                if (array[j] < array[minIndex]) {
                    minIndex = j;
                }
            }
            swap(array,i,minIndex);
        }
    }

    private static void swap(int[] array, int i, int minIndex) {
        int tmp = array[i];
        array[i] = array[minIndex];
        array[minIndex] = tmp;
    }

直接选择排序的特性总结:
1. 直接选择排序思考非常好理解,但是效率不是很好。实际中很少使用
2. 时间复杂度:O(N^2)
3. 空间复杂度:O(1)
4. 稳定性:不稳定


    private static void swap(int[] array, int i, int j) {
        int tmp = array[i];
        array[i] = array[j];
        array[j] = tmp;
    }

    public static void select2Sort(int[] array) {
        int left = 0;
        int right = array.length - 1;
        while(left<right) {
            int minIndex = left;
            int maxIndex = left;
            for (int i = left + 1; i <= right; i++) {
                if(array[i] < array[minIndex]) {
                    minIndex = i;
                }
                if(array[i] > array[maxIndex]) {
                    maxIndex = i;
                }
            }
            swap(array,left,minIndex);
            //第一个数据是最大值
            if(maxIndex == left) {
                maxIndex = minIndex;
            }
            swap(array,right,maxIndex);
            left++;
            right--;
        }
    }
//复杂度不变

2.2堆排序

堆排序(Heapsort)是指利用堆积树(堆)这种数据结构所设计的一种排序算法,它是选择排序的一种。它是通过堆来进行选择数据。需要注意的是排升序要建大堆,排降序建小堆

    public static void heapSort(int[] array) {
        createHeap(array);
        int end = array.length - 1;
        while(end > 0) {
            swap(array,0,end);
            siftDown(array,0,end);
            end--;
        }
    }

    private static void swap(int[] array, int i, int j) {
        int tmp = array[i];
        array[i] = array[j];
        array[j] = tmp;
    }

    public static void createHeap(int[] array) {
        for (int parent = (array.length - 1 - 1) / 2; parent >= 0; parent--) {
            siftDown(array,parent,array.length);
        }
    }

    private static void siftDown(int[] array,int parent,int len) {
        int child = parent * 2 + 1;
        while(child < len) {
            if(child + 1 < len && array[child] < array[child + 1]) {
                child++;
            }
            if(array[child] > array[parent]) {
                swap(array,child,parent);
                parent = child;
                child = parent * 2 + 1;
            }else {
                break;
            }
        }
    }

堆排序的特性总结:
1. 堆排序使用堆来选数,效率就高了很多。
2. 时间复杂度:O(N*logN)
3. 空间复杂度:O(1)
4. 稳定性:不稳定

2.3直接选择排序与堆排序的耗时比较

public class Text {
    public static void main(String[] args) {
        int[] array = new int[100000];
        readyDataOrder(array);
        InsertOrder(array);
        ShellOrder(array);
        selectOrder(array);
        select2Order(array);
        heaptOrder(array);
    }

    public static void readyDataOrder(int[] array) {
        Random random = new Random();
        for (int i = 0; i < array.length; i++) {
            array[i] = random.nextInt(100000);
        }
    }

    public static void InsertOrder(int[] array) {
        array = Arrays.copyOf(array,array.length);
        long start = System.currentTimeMillis();
        Sort.insertSort(array);
        long end = System.currentTimeMillis();
        System.out.println("插入排序耗时参考:" + (end - start));
    }

    public static void ShellOrder(int[] array) {
        array = Arrays.copyOf(array,array.length);
        long start = System.currentTimeMillis();
        Sort.shellSort(array);
        long end = System.currentTimeMillis();
        System.out.println("希尔排序耗时参考:" + (end - start));
    }

    public static void selectOrder(int[] array) {
        array = Arrays.copyOf(array,array.length);
        long start = System.currentTimeMillis();
        Sort.selectSort(array);
        long end = System.currentTimeMillis();
        System.out.println("选择排序耗时参考:" + (end - start));
    }

    public static void select2Order(int[] array) {
        array = Arrays.copyOf(array,array.length);
        long start = System.currentTimeMillis();
        Sort.select2Sort(array);
        long end = System.currentTimeMillis();
        System.out.println("选择排序2耗时参考:" + (end - start));
    }

    public static void heaptOrder(int[] array) {
        array = Arrays.copyOf(array,array.length);
        long start = System.currentTimeMillis();
        Sort.heapSort(array);
        long end = System.currentTimeMillis();
        System.out.println("堆排序耗时参考:" + (end - start));
    }
}
//插入排序耗时参考:2098
//希尔排序耗时参考:25
//选择排序耗时参考:4093
//选择排序2耗时参考:2929
//堆排序耗时参考:16

3.交换排序

所谓交换,就是根据序列中两个记录键值的比较结果来对换这两个记录在序列中的位置,交换排序的特点是:将键值较大的记录向序列的尾部移动,键值较小的记录向序列的前部移动。

3.1冒泡排序

    public static void bubbleSort(int[] array) {
        //i是趟数
        for (int i = 0; i < array.length - 1; i++) {
            for (int j = 0; j < array.length - 1 - i; j++) {
                if(array[j] > array[j+1]) {
                    swap(array,j,j+1);
                }
            }
        }
    }

    private static void swap(int[] array, int i, int j) {
        int tmp = array[i];
        array[i] = array[j];
        array[j] = tmp;
    }

冒泡排序的特性总结:
1. 冒泡排序是一种非常容易理解的排序
2. 时间复杂度:O(N^2)
3. 空间复杂度:O(1)
4. 稳定性:稳定

优化:

    public static void bubbleSort(int[] array) {
        //i是趟数
        for (int i = 0; i < array.length - 1; i++) {
            boolean flg = true;
            for (int j = 0; j < array.length - 1 - i; j++) {
                if(array[j] > array[j+1]) {
                    swap(array,j,j+1);
                    flg = false;
                }
            }
            if(flg) {
                break;
            }
        }
    }

    private static void swap(int[] array, int i, int j) {
        int tmp = array[i];
        array[i] = array[j];
        array[j] = tmp;
    }

3.2快速排序

任取待排序元素序列中的某元素作为基准值,按照该排序码将待排序集合分割成两子序列,左子序列中所有元素均小于基准值,右子序列中所有元素均大于基准值,然后最左右子序列重复该过程,直到所有元素都排列在相应位置上为止

1. Hoare版
    public static void quickSort(int[] array) {
        quick(array,0,array.length - 1);
    }

    private static void quick(int[] array, int left, int right) {
        if(left > right) return;
        int par = partition(array,left,right);
        quick(array,left,par-1);
        quick(array,par+1,right);
    }

    private static int partition(int[] array, int start, int end) {
        int i = start;
        int pivot = array[start];
        while(start < end) {
//先end后start
            while(start < end && array[end] >= pivot) {
                end--;
            }
            while(start < end && array[start] <= pivot) {
                start++;
            }
            swap(array,start,end);
        }
        swap(array,i,start);
        return start;
    }

    private static void swap(int[] array, int i, int j) {
        int tmp = array[i];
        array[i] = array[j];
        array[j] = tmp;
    }

快速排序的特性总结:
1. 快速排序整体的综合性能和使用场景都是比较好的,所以才敢叫快速排序
2. 时间复杂度:O(N*logN)     如果的是1 2 3 4 5 或 5 4 3 2 1…… 单分支的树时间复杂度为O(N^2),但一般不说快排时间复杂度为O(N^2),
3. 空间复杂度:O(logN)   单分支的树空间复杂度为O(N)
4. 稳定性:不稳定

 
//插入排序耗时参考:1983
//希尔排序耗时参考:19
//选择排序耗时参考:3844
//选择排序2耗时参考:2977
//堆排序耗时参考:14
//冒泡排序耗时参考:20525
//快速排序耗时参考:11
2. 挖坑法
    public static void quickSort(int[] array) {
        quick(array,0,array.length - 1);
    }

    private static void quick(int[] array, int left, int right) {
        if(left > right) return;
        int par = partition(array,left,right);
        quick(array,left,par-1);
        quick(array,par+1,right);
    }  

    private static int partition(int[] array,int start,int end) {
        int pivot = array[start];
        while(start < end) {
            while(start < end && array[end] >= pivot) {
                end--;
            }
            array[start] = array[end];
            while(start < end && array[start] <= pivot) {
                start++;
            }
            array[end] = array[start];
        }
        array[start] = pivot;
        return start;
    }
3. 前后指针
    public static void quickSort(int[] array) {
        quick(array,0,array.length - 1);
    }

    private static void quick(int[] array, int left, int right) {
        if(left > right) return;
        int par = partition(array,left,right);
        quick(array,left,par-1);
        quick(array,par+1,right);
    }
    
    private static int partition(int[] array,int start,int end) {
        int prev = start;
        int cur = start + 1;
        while(cur <= end) {
            if(array[cur] < array[start] && array[++prev] != array[cur]) {
                swap(array,cur,prev);
            }
            cur++;
        }
        swap(array,prev,start);
        return prev;
    }

    private static void swap(int[] array, int i, int j) {
        int tmp = array[i];
        array[i] = array[j];
        array[j] = tmp;
    }
3.2.1快速排序的优化
1.三数取中法选key
    private static void quick(int[] array, int left, int right) {
        if(left > right) return;
        int index = midThreeNum(array,left,right);
        swap(array,index,left);
        int par = partition(array,left,right);
        quick(array,left,par-1);
        quick(array,par+1,right);
    }

    //三数取中法选key
    public static int midThreeNum(int[] array, int start, int end) {
        int mid = (start + end) / 2;
        if(array[start] < array[end]) {
            if(array[mid] < array[start]){
                return start;
            }else if(array[mid] > array[end]) {
                return end;
            }else {
                return mid;
            }
        }else {
            if(array[mid] > array[start]){
                return start;
            }else if(array[mid] < array[end]) {
                return end;
            }else {
                return mid;
            }
        }
    }
2. 递归到小的子区间时,可以考虑使用插入排序
    public static void quickSort(int[] array) {
        quick(array,0,array.length - 1);
    }

    private static void quick(int[] array, int left, int right) {
        if(left > right) return;
        if(right - left + 1 == 7) {
            insertSort2(array,left,right);
        }
        int index = midThreeNum(array,left,right);
        swap(array,index,left);
        int par = partition(array,left,right);
        quick(array,left,par-1);
        quick(array,par+1,right);
    }

    //三数取中法选key
    public static int midThreeNum(int[] array, int start, int end) {
        int mid = (start + end) / 2;
        if(array[start] < array[end]) {
            if(array[mid] < array[start]){
                return start;
            }else if(array[mid] > array[end]) {
                return end;
            }else {
                return mid;
            }
        }else {
            if(array[mid] > array[start]){
                return start;
            }else if(array[mid] < array[end]) {
                return end;
            }else {
                return mid;
            }
        }
    }

    //递归到小的子区间时,可以考虑使用插入排序
    private static void insertSort2(int[] array ,int start,int end) {
        for (int i = start + 1; i <= end; i++) {
            int j = i - 1;
            int tmp = array[i];
            for (; j >= start; j--) {
                if (array[j] > tmp) {
                    array[j + 1] = array[j];
                } else {
//                    array[j+1] = tmp;
                    break;
                }
            }
            array[j + 1] = tmp;
        }
    }

    private static int partition(int[] array, int start, int end) {
        int i = start;
        int pivot = array[start];
        while(start < end) {
            while(start < end && array[end] >= pivot) {
                end--;
            }
            while(start < end && array[start] <= pivot) {
                start++;
            }
            swap(array,start,end);
        }
        swap(array,i,start);
        return start;
    }
3.2.2非递归的快速排序
    public static void quickSort2(int[] array) {
        int left = 0;
        int right = array.length - 1;
        int par = partition(array,left,right);
        Stack<Integer> stack = new Stack<>();
        if(par > left + 1) {
            stack.push(left);
            stack.push(par - 1);
        }
        if(par < right - 1) {
            stack.push(par + 1);
            stack.push(right);
        }
        while(!stack.isEmpty()) {
            right = stack.pop();
            left = stack.pop();
            par = partition(array,left,right);
            if(par > left + 1) {
                stack.push(left);
                stack.push(par - 1);
            }
            if(par < right - 1) {
                stack.push(par + 1);
                stack.push(right);
            }
        }
    }

    private static int partition(int[] array, int start, int end) {
        int i = start;
        int pivot = array[start];
        while(start < end) {
            while(start < end && array[end] >= pivot) {
                end--;
            }
            while(start < end && array[start] <= pivot) {
                start++;
            }
            swap(array,start,end);
        }
        swap(array,i,start);
        return start;
    }

4.归并排序

归并排序(MERGE-SORT)是建立在归并操作上的一种有效的排序算法,该算法是采用分治法(Divide andConquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并

1.归并排序 

    public static void mergeSort(int[] array) {
        mergeSortFunc(array,0,array.length - 1);
    }

    private static void mergeSortFunc(int[] array,int left,int right) {
        if(left == right) return;
        int mid =(left + right) / 2;
        //分解
        mergeSortFunc(array,left,mid);
        mergeSortFunc(array,mid+1,right);
        //合并
        merge(array,left,right,mid);
    }

    private static void merge(int[] array, int left, int right, int mid) {
        int s1 = left;
        int e1 = mid;
        int s2 = mid + 1;
        int e2 = right;
        int[] tmpArr = new int[right - left + 1];
        int k = 0;
        while(s1<=e1 && s2<=e2) {
            if(array[s1] < array[s2]) {
                tmpArr[k++] = array[s1++];
            }else {
                tmpArr[k++] = array[s2++];
            }
        }
        while(s1<=e1) {
            tmpArr[k++] = array[s1++];
        }
        while(s2<=e2) {
            tmpArr[k++] = array[s2++];
        }
        for (int i = 0; i < tmpArr.length; i++) {
            array[i+left] = tmpArr[i];
        }
    }

归并排序的特性总结:
1. 归并的缺点在于需要O(N)的空间复杂度,归并排序的思考更多的是解决在磁盘中的外排序问题。
2. 时间复杂度:O(N*logN)
3. 空间复杂度:O(N)
4. 稳定性:稳定

//插入排序耗时参考:2301
//希尔排序耗时参考:25
//选择排序耗时参考:4710
//选择排序2耗时参考:3635
//堆排序耗时参考:25
//冒泡排序耗时参考:23906
//快速排序耗时参考:40
//归并排序耗时参考:42

2.非递归的归并排序 


    public static void mergeSort(int[] array) {
        int gap = 1;
        while (gap < array.length) {
            for (int i = 0; i < array.length; i += 2*gap) {
                int left = i;
                int mid = left + gap - 1;
                int right = mid + gap;
                if (mid >= array.length) {
                    mid = array.length - 1;
                }
                if (right >= array.length) {
                    right = array.length - 1;
                }
                merge(array, left, right, mid);
            }
            gap *= 2;
        }
    }

    private static void merge(int[] array, int left, int right, int mid) {
        int s1 = left;
        int e1 = mid;
        int s2 = mid + 1;
        int e2 = right;
        int[] tmpArr = new int[right - left + 1];
        int k = 0;
        while (s1 <= e1 && s2 <= e2) {
            if (array[s1] < array[s2]) {
                tmpArr[k++] = array[s1++];
            } else {
                tmpArr[k++] = array[s2++];
            }
        }
        while (s1 <= e1) {
            tmpArr[k++] = array[s1++];
        }
        while (s2 <= e2) {
            tmpArr[k++] = array[s2++];
        }
        for (int i = 0; i < tmpArr.length; i++) {
            array[i + left] = tmpArr[i];
        }
    }

3.海量数据的排序问题

外部排序:排序过程需要在磁盘等外部存储进行的排序
前提:内存只有 1G,需要排序的数据有 100G
因为内存中因为无法把所有数据全部放下,所以需要外部排序,而归并排序是最常用的外部排序
1. 先把文件切分成 200 份,每个 512 M
2. 分别对 512 M 排序,因为内存已经可以放的下,所以任意排序方式都可以
3. 进行 2路归并,同时对 200 份有序文件做归并过程,最终结果就有序了

5.排序算法复杂度及稳定性分析

排序方法最好平均最坏空间复杂度稳定性
冒泡排序O(n)O(n^2)O(n^2)O(1)稳定
插入排序O(n)O(n^2)O(n^2)O(1)稳定
选择排序O(n^2)O(n^2)O(n^2)O(1)不稳定
希尔排序O(n)O(n^1.3)O(n^2)O(1)不稳定
堆排序O(n * log(n))O(n * log(n))O(n * log(n))O(1)不稳定
快速排序O(n * log(n))O(n * log(n))O(n^2)O(log(n)) ~ O(n)不稳定
归并排序O(n * log(n))O(n * log(n))O(n * log(n))O(n)稳定

三、其他非基于比较排序

1.计数排序

计数排序的场景一定是数据集中在某各范围中

步骤:

1. 统计相同元素出现次数
2. 根据统计的结果将序列回收到原来的序列中

    public static void countSort(int[] array) {
        //1.求 最大值 最小值 来确定 计数数组的大小
        int min = array[0];
        int max = array[0];
        for (int i = 1; i < array.length; i++) {
            if(array[i] < min) {
                min = array[i];
            }
            if(array[i] > max) {
                max = array[i];
            }
        }
        int len = max - min + 1;
        int[] count = new int[len];
        //2.遍历原来的数组 存放元素到计数数组中
//O(N)
        for (int i = 0; i < array.length; i++) {
            int index = array[i] - min;
            count[index]++;
        }
        //3.遍历计数数组
//O(范围)
        int arrIndex = 0;
        for (int i = 0; i < count.length; i++) {
            while(count[i]!=0) {
                array[arrIndex] = i + min;
                arrIndex++;
                count[i]--;
            }
        }

时间复杂度:O(范围 + N )

空间复杂度:O(范围)

稳定性:稳定

2.基数排序

动态图

队列,桶

3.桶排序

划分多个范围相同的区间,每个子区间自排序,最后合并

    public static void bucketSort(int[] array){

        // 计算最大值与最小值
        int max = array[0];
        int min = array[0];
        for(int i = 1; i < array.length; i++){
            if(array[i] < min) {
                min = array[i];
            }
            if(array[i] > max) {
                max = array[i];
            }
        }

        // 计算桶的数量
        int bucketNum = (max - min) / array.length + 1;
        ArrayList<ArrayList<Integer>> bucketArr = new ArrayList<>(bucketNum);
        for(int i = 0; i < bucketNum; i++){
            bucketArr.add(new ArrayList<Integer>());
        }

        // 将每个元素放入桶
        for(int i = 0; i < array.length; i++){
            int num = (array[i] - min) / (array.length);
            bucketArr.get(num).add(array[i]);
        }

        // 对每个桶进行排序
        for(int i = 0; i < bucketArr.size(); i++){
            Collections.sort(bucketArr.get(i));
        }

        // 将桶中的元素赋值到原序列
        int index = 0;
        for(int i = 0; i < bucketArr.size(); i++){
            for(int j = 0; j < bucketArr.get(i).size(); j++){
                array[index++] = bucketArr.get(i).get(j);
            }
        }
    }

4.比较

  • 基数排序:根据键值的每位数字来分配桶;
  • 计数排序:每个桶只存储单一键值;
  • 桶排序:每个桶存储一定范围的数值;

5.排序

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/890353.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

肺腺癌预后新指标:全切片图像中三级淋巴结构密度的自动化量化|文献精析·24-10-09

小罗碎碎念 本期这篇文章&#xff0c;我去年分享过一次。当时发表在知乎上&#xff0c;没有标记参考文献&#xff0c;配图的清晰度也不够&#xff0c;并且分析的还不透彻&#xff0c;所以趁着国庆假期重新分析一下。 这篇文章的标题为《Computerized tertiary lymphoid structu…

【实战】Nginx+Lua脚本+Redis 实现自动封禁访问频率过高IP

大家好&#xff0c;我是冰河~~ 自己搭建的网站刚上线&#xff0c;短信接口就被一直攻击&#xff0c;并且攻击者不停变换IP&#xff0c;导致阿里云短信平台上的短信被恶意刷取了几千条&#xff0c;加上最近工作比较忙&#xff0c;就直接在OpenResty上对短信接口做了一些限制&am…

《Linux运维总结:基于ARM64+X86_64架构CPU使用docker-compose一键离线部署mongodb 7.0.14容器版分片集群》

总结&#xff1a;整理不易&#xff0c;如果对你有帮助&#xff0c;可否点赞关注一下&#xff1f; 更多详细内容请参考&#xff1a;《Linux运维篇&#xff1a;Linux系统运维指南》 一、部署背景 由于业务系统的特殊性&#xff0c;我们需要面向不通的客户安装我们的业务系统&…

C++入门基础知识110—【关于C++ if...else 语句】

成长路上不孤单&#x1f60a;&#x1f60a;&#x1f60a;&#x1f60a;&#x1f60a;&#x1f60a; 【14后&#x1f60a;///C爱好者&#x1f60a;///持续分享所学&#x1f60a;///如有需要欢迎收藏转发///&#x1f60a;】 今日分享关于C if...else 语句的相关内容&#xff01…

数据结构-5.2.树的性质

一.树的常考性质&#xff1a; 性质1&#xff1a;结点数 总度数 1(结点的度&#xff1a;结点分支的数量) 一个分支中&#xff0c;如父结点B&#xff0c;两个子结点为E和F&#xff0c;结点B的度的值为2&#xff0c;等于子结点数量&#xff0c;加上这一个父结点(父结点只能有一…

部署私有仓库以及docker web ui应用

官方地址&#xff1a;https://hub.docker.com/_/registry/tags 一、拉取registry私有仓库镜像 docker pull registry:latest 二、运⾏容器 docker run -itd -v /home/dockerdata/registry:/var/lib/registry --name "pri_registry1" --restartalways -p 5000:5000 …

数据结构-5.5.二叉树的存储结构

一.二叉树的顺序存储&#xff1a; a.完全二叉树&#xff1a; 1.顺序存储中利用了静态数组&#xff0c;空间大小有限&#xff1a; 2.基本操作&#xff1a; (i是结点编号) 1.上述图片中i所在的层次后面的公式应该把n换成i(图片里写错了)&#xff1b; 2.上述图片判断i是否有左…

ClickHouse的原理及使用,

1、前言 一款MPP查询分析型数据库——ClickHouse。它是一个开源的&#xff0c;面向列的分析数据库&#xff0c;由Yandex为OLAP和大数据用例创建。ClickHouse对实时查询处理的支持使其适用于需要亚秒级分析结果的应用程序。ClickHouse的查询语言是SQL的一种方言&#xff0c;它支…

网络安全之XXE攻击

0x01 什么是 XXE 个人认为&#xff0c;XXE 可以归结为一句话&#xff1a;构造恶意 DTD 介绍 XXE 之前&#xff0c;我先来说一下普通的 XML 注入&#xff0c;这个的利用面比较狭窄&#xff0c;如果有的话应该也是逻辑漏洞。 既然能插入 XML 代码&#xff0c;那我们肯定不能善罢…

图像分类-demo(Lenet),tensorflow和Alexnet

目录 demo(Lenet) 代码实现基本步骤&#xff1a; TensorFlow 一、核心概念 二、主要特点 三、简单实现 参数: 模型编译 模型训练 模型评估 Alexnet model.py train.py predict.py demo(Lenet) PyTorch提供了一个名为“torchvision”的附加库&#xff0c;其中包含…

【在Linux世界中追寻伟大的One Piece】信号捕捉|阻塞信号

目录 1 -> 信号捕捉初识 2 -> 阻塞信号 2.1 -> 信号其他相关常见概念 2.2 -> 在内核中的表示 2.3 -> sigset_t 2.4 -> 信号集操作函数 2.5 -> sigprocmask 2.6 -> sigpending 3 -> 捕捉信号 3.1 -> 内核如何实现信号的捕捉 3.2 ->…

VBA高级应用30例应用3Excel中的ListObject对象:选择表的一部分

《VBA高级应用30例》&#xff08;版权10178985&#xff09;&#xff0c;是我推出的第十套教程&#xff0c;教程是专门针对高级学员在学习VBA过程中提高路途上的案例展开&#xff0c;这套教程案例与理论结合&#xff0c;紧贴“实战”&#xff0c;并做“战术总结”&#xff0c;以…

【Spring】获取 Cookie和Session

回顾 Cookie HTTP 协议自身是属于“无状态”协议 无状态&#xff1a;默认情况下&#xff0c;HTTP 协议的客户端和服务器之间的这次通信和下次通信之间没有直接的联系 但是在实际开发中&#xff0c;我们很多时候是需要知道请求之间的关联关系的 例如登录网站成功后&#xff…

Linux高效查日志命令介绍

说明&#xff1a;之前介绍Linux补充命令时&#xff0c;有介绍使用tail、grep命令查日志&#xff1b; Linux命令补充 今天发现仅凭这两条命令不够&#xff0c;本文扩展介绍一下。 命令一&#xff1a;查看日志开头 head -n 行数 日志路径如下&#xff0c;可以查看程序启动是否…

安装和配置k8s可视化UI界面dashboard-1.20.6

安装和配置k8s可视化UI界面dashboard-1.20.6 1.环境规划2.初始化服务器1&#xff09;配置主机名2&#xff09;设置IP为静态IP3&#xff09;关闭selinux4&#xff09;配置主机hosts文件5&#xff09;配置服务器之间免密登录6&#xff09;关闭交换分区swap&#xff0c;提升性能7&…

系统架构设计师考试背记精要

1、架构的本质&#xff1a; &#xff08;1&#xff09;软件架构为软件系统提供了一个结构、行为和属性的高级抽象。&#xff08;2&#xff09;软件架构风格是特定应用领域的惯用模式&#xff0c;架构定义一个词汇表和一组约束。 2、数据流风格&#xff1a;适合于分阶段做数据处…

Springboot从入门到起飞-【day01】

个人主页→VON 收录专栏→Springboot从入门到起飞 一、前言 经过了近两个月的沉淀开始了新专栏的学习&#xff0c;经过深思熟虑还是决定重新学习java&#xff0c;因为基础部分东西太多太乱就不进行逐一的更新了&#xff0c;等到学完了一同进行更新。 二、Springboot简要概述 …

汽车免拆诊断案例 | 2013款宝马116i车偶尔加速不良

故障现象  一辆2013款宝马116i车&#xff0c;搭载N13B16A 发动机&#xff0c;累计行驶里程约为12.1万km。车主反映&#xff0c;该车行驶中偶尔加速无反应&#xff0c;且发动机故障灯异常点亮。 故障诊断 接车后试车&#xff0c;故障现象无法再现。用故障检测仪检测&#xff…

ChatGPT国内中文版镜像网站整理合集(2024/10/06)

一、GPT中文镜像站 ① yixiaai.com 支持GPT4、4o以及o1&#xff0c;支持MJ绘画 ② chat.lify.vip 支持通用全模型&#xff0c;支持文件读取、插件、绘画、AIPPT ③ AI Chat 支持GPT3.5/4&#xff0c;4o以及MJ绘画 1. 什么是镜像站 镜像站&#xff08;Mirror Site&#xff…

A股知识答题pk小程序怎么做?

A股知识答题pk小程序怎么做&#xff1f;以下是制作A股知识答题PK小程序的一般步骤&#xff1a; 一、 需求分析与规划&#xff1a; 明确目标&#xff1a;确定小程序的主要目标&#xff0c;比如是为了帮助用户学习A股知识、进行趣味竞赛&#xff0c;还是作为金融教育工具等。 …