常见算法(Java)


文章目录

  • 常见算法(Java)
  • 查找算法
  • 基本查找
  • 二分查找(折半查找)
  • 插值查找
  • 分块查找
  • 排序算法
  • 冒泡排序
  • 选择排序
  • 插入排序
  • 快速排序


查找算法

基本查找

基本思想:顺序查找也称为线形查找,属于无序查找算法。从数据结构线的一端开始,顺序扫描,依次将遍历到的结点与要查找的值相比较,若相等则表示查找成功;若遍历结束仍没有找到相同的,表示查找失败。

示例代码

public class Teat {
    public static void main(String[] args) {
        int[] arr={121,123,433,435,567,867};
        int number=34;
        System.out.println(basicSearch(arr,number));
    }
    public static boolean basicSearch(int[] arr,int number){
        //利用基本查找来查找number在数组中是否存在
        for (int i = 0; i < arr.length; i++) {
            if (arr[i]==number){
                return true;
            }
        }
        return false;
    }
}

二分查找(折半查找)

前提条件: 数组中的数据必须是有序的

如果是无序的,也可以先进行排序。但是排序之后,会改变原有数据的顺序,查找出来元素位置跟原来的元素可能是不一样的,所以排序之后再查找只能判断当前数据是否在容器当中,返回的索引无实际的意义。

核心逻辑: 每一次都会排出一半的查找范围

优势: 提高查找效率

  1. min和max表示当前要查找的范围
  2. mid是在min和max之间的
  3. 如果要查找的元素在mid的左边,缩小范围时,min不变,max等于mid减1
  4. 如果要查找的元素在mid的右边,缩小范围时,max不变,min等于mid加1

步骤

  • 首先确定数组的首位和末位
//定义两个变量记录要查找的范围
int min = 0;
int max = arr.length - 1;

在数组中首位是0,末位是数组长度-1

  • 接着需要确定中间值
//找到min和max的中间位置
int mid = (min + max) / 2;
  • 然后根据情况进行min和max的移动
//拿着min指向的元素跟要查找的元素进行比较
//number跟mid指向的元素一样
if (arr[mid] > number) {
    //number在mid的左边
    //min不变 max=mid-1
    max = mid - 1;
} else if (arr[mid] < number) {
    //number在mid的右边
    //min不变 max=mid+1
    min = mid + 1;
} else if (arr[mid] == number) {
    //number跟mid指向的值一样
    return mid;
}
  • 接着确定终止条件
if (min > max) {
    return -1;
}

当min大于max的时候意味着循环已经结束,并且要查找的数据不在数组中,因此返回-1

  • 最后确定循环语句
while (true) {
    if (min > max) {
        return -1;
    }
    //找到min和max的中间位置
    int mid = (min + max) / 2;
    //拿着min指向的元素跟要查找的元素进行比较
    //number跟mid指向的元素一样
    if (arr[mid] > number) {
        //number在mid的左边
        //min不变 max=mid-1
        max = mid - 1;
    } else if (arr[mid] < number) {
        //number在mid的右边
        //min不变 max=mid+1
        min = mid + 1;
    } else if (arr[mid] == number) {
        //number跟mid指向的值一样
        return mid;
    }
}

在这个查找中,无法确定循环的具体次数,但是知道循环的具体条件,因此直接使用while(true)来做循环。

将mid放到循环体里面是因为每次循环结束都会需要重新计算mid的值

示例代码

public class BinarySearch {
    public static void main(String[] args) {
        int[] arr = {1, 13, 23, 79, 81, 183, 137, 139};
        System.out.println(binarySearch(arr, 79));
    }

    public static int binarySearch(int[] arr, int number) {
        //定义两个变量记录要查找的范围
        int min = 0;
        int max = arr.length - 1;
        //利用循环不断的去找要查找的数据
        while (true) {
            if (min > max) {
                return -1;
            }
            //找到min和max的中间位置
            int mid = (min + max) / 2;
            //拿着min指向的元素跟要查找的元素进行比较
            //number跟mid指向的元素一样
            if (arr[mid] > number) {
                //number在mid的左边
                //min不变 max=mid-1
                max = mid - 1;
            } else if (arr[mid] < number) {
                //number在mid的右边
                //min不变 max=mid+1
                min = mid + 1;
            } else if (arr[mid] == number) {
                //number跟mid指向的值一样
                return mid;
            }
        }
    }
}

插值查找

前提条件: 数组中的数据必须是有序的,并且分布较为均匀

基本思想: 基于二分查找算法,将查找点的选择改进为自适应选择,可以提升查找算法的平均性能比折半查找要好的多。

优势: 相对于二分查找,这种插值查找可以更快的在有序均匀数组中快速的查找数据

与二分查找的区别: mid的公式发生了变化
java代码查找ldap cn java查找算法有哪些_数据结构
这个公式的数学基础是等差数列

步骤

  • 首先确定数组的首位和末位
//定义两个变量记录要查找的范围
int low = 0;
int high = arr.length - 1;

在数组中首位是0,末位是数组长度-1

  • 接着需要确定中间值
//找到min和max的中间位置
int mid = low + (key - arr[low]) * (high - low) / (arr[high] - arr[low]);
int midVal = arr[mid];
  • 然后根据情况进行low和high的移动
//拿着min指向的元素跟要查找的元素进行比较
//number跟mid指向的元素一样
if (midVal > key) {
    //key在mid的左边
    //low不变 high=mid-1
    high = mid - 1;
} else if (midVal < key) {
    //key在mid的右边
    //high不变 low=mid+1
    low = mid + 1;
} else if (midVal == key) {
    //key跟mid指向的值一样
    return mid;
}
  • 接着确定终止条件
if (low > high || key > arr[high] || key < arr[low]) {
    return -1;
}

当low大于high的时候意味着循环已经结束,并且要查找的数据不在数组中,因此返回-

当key大于数组的最大值的时候,表示key不在数组内,直接返回-1,结束循环

当key小于数组的最小值的时候,表示key不在数组内,直接返回-1,结束循环

  • 最后确定循环语句
while (true) {
    if (low > high || key > arr[high] || key < arr[low]) {
        return -1;
    }
    //找到min和max的中间位置
    int mid = low + (key - arr[low]) * (high - low) / (arr[high] - arr[low]);
    int midVal = arr[mid];
    //拿着min指向的元素跟要查找的元素进行比较
//number跟mid指向的元素一样
if (midVal > key) {
    //key在mid的左边
    //low不变 high=mid-1
    high = mid - 1;
} else if (midVal < key) {
    //key在mid的右边
    //high不变 low=mid+1
    low = mid + 1;
} else if (midVal == key) {
    //key跟mid指向的值一样
    return mid;
}
}

在这个查找中,无法确定循环的具体次数,但是知道循环的具体条件,因此直接使用while(true)来做循环。

将mid放到循环体里面是因为每次循环结束都会需要重新计算mid的值

代码示例

public class insertValueSearch {
    public static void main(String[] args) {
        int[] arr = {2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22};
        System.out.println(insertValueSearch(arr, 13));
    }

    public static int insertValueSearch(int[] arr, int key) {
        int low = 0;
        int high = arr.length - 1;
        while (true) {
            if (low > high || key > arr[high] || key < arr[low]) {
                return -1;
            }
            int mid = low + (key - arr[low]) * (high - low) / (arr[high] - arr[low]);
            int midVal = arr[mid];
            //key在mid对应的值的左边的
            if (midVal > key) {
                high = mid - 1;
            } else if (midVal < key) {
                low = mid + 1;
            } else if (midVal == key) {
                return mid;
            }
        }
    }
}

分块查找

分块的原则1: 前一块的最大数据,小于后一块中所有的数据(块内无序,块间有序)

分块的原则2: 块数数量一半等于数字的个数开根号,比如:16开根号一般分为4块左右

核心思路: 先确定要查找的元素在哪一块,然后在块内挨个查找

块的表达

class Block{
int max;//块中的最大值
int startIndex;//起始索引
int endIndex;//结束索引
}

步骤

  • 先创建块,块中要含有三个变量分别表达块中的最大值,块中起始索引,块中的结束索引
class Block {
    private int max;
    private int startIndex;
    private int endIndex;

    public Block() {
    }

    public Block(int max, int startIndex, int endIndex) {
        this.max = max;
        this.startIndex = startIndex;
        this.endIndex = endIndex;
    }

    public int getMax() {
        return max;
    }

    public void setMax(int max) {
        this.max = max;
    }

    public int getStartIndex() {
        return startIndex;
    }

    public void setStartIndex(int startIndex) {
        this.startIndex = startIndex;
    }

    public int getEndIndex() {
        return endIndex;
    }

    public void setEndIndex(int endIndex) {
        this.endIndex = endIndex;
    }
}
  • 将数组分块,并且按照分块进行创建块对象,并将这些对象放到一个索引数组中
//创建三个块的对象
Block b1 = new Block(21, 0, 5);
Block b2 = new Block(45, 6, 11);
Block b3 = new Block(73, 12, 17);
//定义数组用来管理三个块的对象(索引表)
Block[] blocks = {b1, b2, b3};
  • 在获取索引之前首先要确定要查找数据所在的块的对象
static int findBlock(Block[] blocks, int number) {
    for (int i = 0; i < blocks.length; i++) {
        if (number <= blocks[i].getMax()) {
            return i;
        }
    }
    //将索引表数组中对象的最大值遍历完后依然没有找到比要查找数据大的最大值,此时返回-1
    return -1;
}

查找数据所在块的对象的原理就是根据块中的最大值是否大于要查找的数据。因为块中的最大值要比下一个块中的最小值小同时块中的最小值要比上一个块的最大值大,所以再根据循环的规则来看,如果一个数据比这个块中的最大值小,就说明这个数据已经比上一个块中的最大值大了,进而证明了这个数据处于这个块的范围中

  • 确定了数据在哪个块中就要接着遍历哪个块,然后找出相对应的索引
private static int getIndex(Block[] blocks, int[] arr, int number) {
    //获取所在分块
    int blockIndex = findBlock(blocks, number);
    //如果返回值是-1,说明将索引表数组中对象的最大值遍历完后依然没有找到比要查找数据大的最大值
    if (blockIndex == -1) {
        return -1;
    }
    //如果不是-1,就要获取对应块的起始索引,结束索引,为后面的遍历做准备
    int startIndex = blocks[blockIndex].getStartIndex();
    int endIndex = blocks[blockIndex].getEndIndex();
    
    //遍历
    for (int i = startIndex; i < endIndex; i++) {
        if (arr[i]==number){
            return i;
        }
    }
    //如果循环结束后还没有找到相应的索引,说明不在这些数据中,直接返回-1
    return -1;
}

代码示例

public class BlockSearch {
    public static void main(String[] args) {
        int[] arr = {16, 5, 9, 12, 21, 18,
                32, 23, 37, 26, 45, 34,
                50, 48, 61, 52, 73, 66};
        //创建三个块的对象
        Block b1 = new Block(21, 0, 5);
        Block b2 = new Block(45, 6, 11);
        Block b3 = new Block(73, 12, 17);
        //定义数组用来管理三个块的对象(索引表)
        Block[] blocks = {b1, b2, b3};
        //定义一个变量用来记录要查找的元素
        int number = 48;
        //调用方法,传递索引表,数组,要查找的元素
        int index = getIndex(blocks, arr, number);
        //打印一下
        System.out.println(index);
    }

    //利用分块查找的原理,查找number的索引
    private static int getIndex(Block[] blocks, int[] arr, int number) {
        //获取所在分块
        int blockIndex = findBlock(blocks, number);
        //不在范围内
        if (blockIndex == -1) {
            return -1;
        }
        
        int startIndex = blocks[blockIndex].getStartIndex();
        int endIndex = blocks[blockIndex].getEndIndex();
        int max = blocks[blockIndex].getMax();

        //遍历
        for (int i = startIndex; i < endIndex; i++) {
            if (arr[i]==number){
                return i;
            }
        }
        return -1;
    }

    //定义一个方法,用来确定number在哪一块当中
    static int findBlock(Block[] blocks, int number) {
        for (int i = 0; i < blocks.length; i++) {
            if (number <= blocks[i].getMax()) {
                return i;
            }
        }
        return -1;
    }

}

class Block {
    private int max;
    private int startIndex;
    private int endIndex;

    public Block() {
    }

    public Block(int max, int startIndex, int endIndex) {
        this.max = max;
        this.startIndex = startIndex;
        this.endIndex = endIndex;
    }

    public int getMax() {
        return max;
    }

    public void setMax(int max) {
        this.max = max;
    }

    public int getStartIndex() {
        return startIndex;
    }

    public void setStartIndex(int startIndex) {
        this.startIndex = startIndex;
    }

    public int getEndIndex() {
        return endIndex;
    }

    public void setEndIndex(int endIndex) {
        this.endIndex = endIndex;
    }
}

排序算法

冒泡排序

它重复的遍历过要排序的数列,一次比较相邻的两个元素,如果他们的顺序错误就把他们交换过来。

如果数组中有n个数据,只需要执行n-1论代码

java代码查找ldap cn java查找算法有哪些_排序算法_02

顺序错误发生交换

int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;

temp作为中间变量,用来存储数据,在交换的时候起到一个中转的作用

遍历一次排序数组

for (int j = 0; j < arr.length - 1; j++) {
    if (arr[j] > arr[j + 1]) {
        int temp = arr[j];
        arr[j] = arr[j + 1];
        arr[j + 1] = temp;
    }
}

循环语句中的-1是为了防止数组越界

第二次遍历数组,进行排序

for (int j = 0; j < arr.length - 1-1; j++) {
    if (arr[j] > arr[j + 1]) {
        int temp = arr[j];
        arr[j] = arr[j + 1];
        arr[j + 1] = temp;
    }
}

第二次中遍历数组进行排序的过程中,比以第一次还多了一个-1,这里的-1是因为第一次遍历排序后已经把这组数据中的最大数放到了最后面。一次类推,每次循环结束都会把剩下的数据中的最大值放到后面,因此只需要遍历后面的数据即可,也就是每次遍历的长度是上一次遍历长度-1.

多次遍历数组,进行排序

for (int i = 0; i < arr.length - 1; i++) {
    for (int j = 0; j < arr.length - 1 - i; j++) {
        if (arr[j] > arr[j + 1]) {
            int temp = arr[j];
            arr[j] = arr[j + 1];
            arr[j + 1] = temp;
        }
    }
}

根据第二次循环总结的规律来看,我们只需要遍历数组长度-1次,并且每次便利的长度都比上次遍历长度-1

代码示例

public class BubbleDemo1 {
    public static void main(String[] args) {
        //1.定义数组
        int[] arr = {2, 4, 5, 3, 1};
        //2.利用冒泡排序将数组中的数据变成 1 2 3 4 5        
        //外循环:表示我要执行多少轮。 如果有n个数据,那么执行n - 1 轮
        for (int i = 0; i < arr.length - 1; i++) {
            //内循环:每一轮中我如何比较数据并找到当前的最大值
            //-1:为了防止索引越界
            //-i:提高效率,每一轮执行的次数应该比上一轮少一次。
            for (int j = 0; j < arr.length - 1 - i; j++) {
                //i 依次表示数组中的每一个索引:0 1 2 3 4
                if (arr[j] > arr[j + 1]) {
                    int temp = arr[j];
                    arr[j] = arr[j + 1];
                    arr[j + 1] = temp;
                }
            }
        }
        for (int i = 0; i < arr.length; i++) {
            System.out.println(arr[i]);
        }
    }
}

选择排序

  1. 从0索引开始,跟后面的元素一一比较
  2. 小的放前面,大的放后面
  3. 第一次循环结束后,最小的数据已经确定
  4. 第二次循环从1索引开始以此类推
  5. 第三轮循环从2索引开始以此类推
  6. 第四轮循环从3索引开始以此类推。

java代码查找ldap cn java查找算法有哪些_java代码查找ldap cn_03

根据步骤进行第一次的排序

for (int i = 0 + 1; i < arr.length; i++) {
    if(arr[0]>arr[i]){
        int temp=arr[0];
        arr[0]=arr[i];
        arr[i]=temp;
    }
}

第一排序是从下标1开始往后,和下标0的数据进行比较,只要数据不符合大小比较就会进行替换,将两个数据进行位置转换。所以 i 的初值是0+1,表示从0的下一个下标开始

根据步骤进行第二次排序

for (int i = 1 + 1; i < arr.length; i++) {
    if (arr[1] > arr[i]) {
        int temp = arr[1];
        arr[1] = arr[i];
        arr[i] = temp;
    }
}

还是根据第一次排序的逻辑来看,这次是从下标2开始,和下标1的数据进行比较只要数据不符合大小比较规则就会发生替换,将两个数据的位置进行转换,所以 i 的初值是1+1,表示从1的下一个开始

多次遍历数组进行排序

for (int i = 0; i < arr.length-1; i++) {
    for (int j = i + 1; j < arr.length; j++) {
        if (arr[i] > arr[j]) {
            int temp = arr[i];
            arr[i] = arr[j];
            arr[j] = temp;
        }
    }
}

外循环的-1也还是因为放置数组越界

代码示例

public class SelectionDemo {
    public static void main(String[] args) {
        //1.定义数组
        int[] arr = {2, 4, 5, 3, 1};
        //外循环:几轮
        //i:表示这一轮中,我拿着哪个索引上的数据跟后面的数据进行比较并交换
        for (int i = 0; i < arr.length-1; i++) {
            //内循环:每一轮我要干什么事情?
            //拿着i跟i后面的数据进行比较交换
            for (int j = i + 1; j < arr.length; j++) {
                if (arr[i] > arr[j]) {
                    int temp = arr[i];
                    arr[i] = arr[j];
                    arr[j] = temp;
                }
            }
        }
        for (int i = 0; i < arr.length; i++) {
            System.out.println(arr[i]);
        }
    }
}

插入排序

将0索引的元素到N索引的元素看做是有序的,把N+1索引的元素到最后一个当成是无序的。

遍历无序的数据,将遍历到的元素插入有序序列中适当的位置,如遇到相同数据,插在后面。

N的范围:0~最大索引

java代码查找ldap cn java查找算法有哪些_算法_04

就像是打扑克的时候将手中的牌理顺一样。拿到一张牌,首先是跟已经排好的那部分的最后一张牌进行大小比较,如果刚抽到的牌比最后一张牌大,就会放到最后一张牌的后面,如果比最后一张牌小,就会跟倒数第二张牌进行比较,如果比倒数第二张牌大就直接插到倒数第二张和倒数第一张之间,如果比倒数第二张牌小就会与倒数第三张牌进行比较,以此类推,一直到第一张牌,如果抽到的牌比第一张牌还要下,就直接插到第一张牌前面

步骤

  • 在给到的数据中,确定已有的从开头开始的有序部分,根据有序部分找到无序部分的起始位置
int startIndex = -1;
for (int i = 0; i < arr.length; i++) {
    if (arr[i] > arr[i + 1]) {
        startIndex = i + 1;
        break;
    }
}

遍历整个数组,在遍历的过程中要对大小进行判断。以从小大大为例,如果某位的数大于后面的数,说明该位就是有序部分的最后一位,那下一位就是无序的初始位,此时就要将下一位的数下标返回给startIndex,同时结束遍历

  • 模拟插值
while (j > 0 && arr[j] < arr[j - 1]) {
    int temp = arr[j];
    arr[j] = arr[j - 1];
    arr[j - 1] = temp;
    j--;
}

j对应无序部分的开始下标,那arr[j]就可以理解为扑克牌中刚刚抽到的那张牌,接下类就是进行插值排序的核心,其实通过扑克排序的过程可以看出来这其实就是一个循环,而且是一种知道结束条件得循环,此时就可以用while()循环进行表示,循环的条件是j大于0,并且arr[j]要小于arr[j-1]j大于0类似扑克排序中这张牌对应的位置不是第一张牌,arr[j]<arr[j-1]类似扑克排序中的比较条件,如果这张牌要比调换位置时的下一张牌还要小,并且这张牌的位置还没有到第一张牌的位置,此时就可以进行调换位置的操作,调换位置结束后还要将j-1,作为下一次是否要进行位置替换的预备条件

  • 对插值进行循环,遍历无序的部分
for (int i = startIndex; i < arr.length; i++) {
    int j = i;
    while (j > 0 && arr[j] < arr[j - 1]) {
        int temp = arr[j];
        arr[j] = arr[j - 1];
        arr[j - 1] = temp;
        j--;
    }
}

这里的循环是从startIndex开始的,也就是说遍历无序的部分就行,这里将i的值给了j是避免插值过程中导致i的值发生变化,进而导致循环被打乱

代码示例

public class InsertDemo {
    public static void main(String[] args) {
        int[] arr = {3, 44, 38, 5, 47, 15, 36, 26, 27, 2, 46, 4, 19, 50, 48};
        //找到无序列表的开端
        //1.找到无序的哪一组数组是从哪个索引开始的。
        int startIndex = -1;
        for (int i = 0; i < arr.length; i++) {
            if (arr[i] > arr[i + 1]) {
                startIndex = i + 1;
                break;
            }
        }
        //2.遍历从startIndex开始到最后一个元素,依次得到无序的哪一组数据中的每一个元素
        for (int i = startIndex; i < arr.length; i++) {
            //记录当前要插入数据的索引
            int j = i;
            while (j > 0 && arr[j] < arr[j - 1]) {
                //交换位置
                int temp = arr[j];
                arr[j] = arr[j - 1];
                arr[j - 1] = temp;
                j--;
            }
        }
        //输出
        for (int i = 0; i < arr.length; i++) {
            System.out.println(arr[i]);
        }
    }
}

快速排序

步骤

  1. 从数列中挑出一个元素,一般都是左边第一个数字,称为 “基准数”;
  2. 创建两个指针,一个从前往后走,一个从后往前走。
  3. 先执行后面的指针,找出第一个比基准数小的数字
  4. 再执行前面的指针,找出第一个比基准数大的数字
  5. 交换两个指针指向的数字
  6. 直到两个指针相遇
  7. 将基准数跟指针指向位置的数字交换位置,称之为:基准数归位。
  8. 第一轮结束之后,基准数左边的数字都是比基准数小的,基准数右边的数字都是比基准数大的。
  9. 把基准数左边看做一个序列,把基准数右边看做一个序列,按照刚刚的规则递归排序

java代码查找ldap cn java查找算法有哪些_数据结构_05

  • 按照步骤模拟第一轮的排序
public static void QuickSort(int[] arr, int i, int j) {
    int start = i;
    int end = j;
    //基准数
    int baseNumber = arr[i];
    while (end != start) {
        while (true) {
            if (end <= start || arr[end] < baseNumber) {
                break;
            }
            end--;
        }
        while (true) {
            if (end <= start || arr[start] > baseNumber) {
                break;
            }
            start++;
        }
        int temp = arr[start];
        arr[start] = arr[end];
        arr[end] = temp;
    }
    int temp = arr[i];
    arr[i] = arr[start];
    arr[start] = temp;
}
  • 创建两个指针,一个往前,走指到的数比基准数大就停止,一个往后走,指到的数比基准数小就停止
public static void QuickSort(int[] arr, int i, int j) {
    int start = i;
    int end = j;
    //基准数
    int baseNumber = arr[i];
        while (true) {
            if (end <= start || arr[end] < baseNumber) {
                break;
            }
            end--;
        }
        while (true) {
            if (end <= start || arr[start] > baseNumber) {
                break;
            }
            start++;
        }
}

这里是因为从小到大排序,所以要先移动尾指针再移动头指针

  • 当两个指针都指到了相应的数字的时候,就可以让两个指针指到数字进行替换
public static void QuickSort(int[] arr, int i, int j) {
    int start = i;
    int end = j;
    //基准数
    int baseNumber = arr[i];
        while (true) {
            if (end <= start || arr[end] < baseNumber) {
                break;
            }
            end--;
        }
        while (true) {
            if (end <= start || arr[start] > baseNumber) {
                break;
            }
            start++;
        }
    	int temp = arr[start];
		arr[start] = arr[end];
		arr[end] = temp;
}
  • 要一直这样的进行下去,直到两个指针的位置相等了
public static void QuickSort(int[] arr, int i, int j) {
    int start = i;
    int end = j;
    //基准数
    int baseNumber = arr[i];
    while (end != start) {
        while (true) {
            if (end <= start || arr[end] < baseNumber) {
                break;
            }
            end--;
        }
        while (true) {
            if (end <= start || arr[start] > baseNumber) {
                break;
            }
            start++;
        }
        int temp = arr[start];
        arr[start] = arr[end];
        arr[end] = temp;
    }
}
  • 循环结束此时,刚开始的位置上的数字还没有发生变化,但是其余部分的内容已经按照以这两个指针最终位置为界,指针左边小于基准数,指针右边大于基准数的方式进行排列,此时,需要将基准数归位
public static void QuickSort(int[] arr, int i, int j) {
    int start = i;
    int end = j;
    //基准数
    int baseNumber = arr[i];
    while (end != start) {
        while (true) {
            if (end <= start || arr[end] < baseNumber) {
                break;
            }
            end--;
        }
        while (true) {
            if (end <= start || arr[start] > baseNumber) {
                break;
            }
            start++;
        }
        int temp = arr[start];
        arr[start] = arr[end];
        arr[end] = temp;
    }
    int temp = arr[i];
    arr[i] = arr[start];
    arr[start] = temp;
}

此时数据已经完成第一轮的快速排序

  • 将第一轮的快速排序变成递归的方式,用这种方式不断缩小排序的范围,从而达到快速排序的目的
public static void QuickSort(int[] arr, int i, int j) {
    int start = i;
    int end = j;
    if (start > end) {
        return;
    }
    int baseNumber = arr[i];
    while (end != start) {
        while (true) {
            if (end <= start || arr[end] < baseNumber) {
                break;
            }
            end--;
        }
        while (true) {
            if (end <= start || arr[start] > baseNumber) {
                break;
            }
            start++;
        }
        int temp = arr[start];
        arr[start] = arr[end];
        arr[end] = temp;
    }
    int temp = arr[i];
    arr[i] = arr[start];
    arr[start] = temp;
    QuickSort(arr, i, start - 1);
    QuickSort(arr, start + 1, j);
}

递归的结束条件是头指针大于尾指针,也就意味着如果此时在不停止,就会让已经排好的顺序打乱,所以此时需要结束递归,这个条件需要写到确定基准数之前,否则会出现越界问题,这里的递归主要是根据快速排序是让一边的数据比基准数小一边的数据比基准数大来确定的,因此要用到两个,一个是进行右边的快速排序,一个是进行左边的快速排序,在两个快速排序中,又再次分为了两边,一直进行下去。一直到不能再小的时候进行折返,完成数据的排序

代码示例

public class QuickSort {
    public static void main(String[] args) {
        int[] arr = {1, 6, 2, 7, 9, 3, 4, 5, 10, 8};
        QuickSort(arr, 0, arr.length - 1);
        for (int i = 0; i < arr.length; i++) {
            System.out.println(arr[i]);
        }
    }

    public static void QuickSort(int[] arr, int i, int j) {
        //定义两个变量记录要查找的范围
        int start = i;
        int end = j;
        if (start > end) {
            //递归的出口
            return;
        }
        //记录基准数
        int baseNumber = arr[i];
        //利用循环找到要交换的数字
        while (end != start) {
            //利用end,从后往前开始找,找比基准数小的数字
            while (true) {
                if (end <= start || arr[end] < baseNumber) {
                    break;
                }
                end--;
            }
            //利用start,从前往后找,找比基准数大的数字
            while (true) {
                if (end <= start || arr[start] > baseNumber) {
                    break;
                }
                start++;
            }
            //把end和start指向的元素进行交换
            int temp = arr[start];
            arr[start] = arr[end];
            arr[end] = temp;
        }
        //当start和end指向了同一个元素的时候,那么上面的循环就会结束
        //表示已经找到了基准数在数组中应存入的位置
        //基准数归位
        //就是拿着这个范围中的第一个数字,跟start指向的元素进行交换
        int temp = arr[i];
        arr[i] = arr[start];
        arr[start] = temp;
        //确定6左边的范围,重复刚刚所做的事情
        QuickSort(arr, i, start - 1);
        //确定6右边的范围,重复刚刚所做的事情
        QuickSort(arr, start + 1, j);
    }
}