算法
什么是算法?
系统解决一类问题的策略,就叫做算法,也是方法的一种,所以一种算法专注于一种作用或者说能力。那么排序算法就是排列次序的方法。
二分查找
二分查找原理:定义一个中间值,然后判断中间值和目标值的大小关系,中间值>目标值则在左半部分查找,中间值<目标值,则在右半部分查找;然后不断缩小查找区间,锁定查找值。前提:这是在有序范围中查找,无序范围准确度看命(个人理解)
官话:二分查找也称折半查找(Binary Search),它是一种效率较高的查找方法。首先,假设表中元素是按升序排列,将表中间位置记录的关键字与查找关键字比较,如果两者相等,则查找成功;否则利用中间位置记录将表分成前、后两个子表,如果中间位置记录的关键字大于查找关键字,则进一步查找前一子表,否则进一步查找后一子表。重复以上过程,直到找到满足条件的记录,使查找成功,或直到子表不存在为止,此时查找不成功。
代码实现:
public class Demo{
public static void main(String[] args) {
int[] arr = {1, 2, 3, 4, 5, 6, 7, 8, 9};
int num = 3;
// 定义左右边界
int left = 0;
int right = arr.length - 1;
// 遍历数组
for (int i = 0; i < arr.length; i++) {
// 定义中间值下标
int mid = (right + left) >> 1;
// 判断目标值是否等于中间值
if (arr[mid] == num) {
System.out.println(mid);
break;
}
// 如果目标值小于中间值,肯定在左半部分查找啊
if (arr[mid] > num) {
right = mid - 1;
}
// 如果目标值大于中间值,肯定在右半部分查找啊
if (arr[mid] < num) {
left = mid + 1;
}
}
}
}
冒泡排序
冒泡排序原理:(升序)让第一个元素与它后一个元素比较大小,若第一个元素大于第二个元素,则他们交换位置,交换位置后此元素继续和下一个元素进行比较;反之若第一个元素小于第二个元素,则本轮(内层)循环结束,从下一个元素开始循环遍历,遍历长度减一轮直至数组顺序完成排列。
代码举例
public class Demo{
public static void main(String[] args){
int[] arrays = {10, 30, 99, 58, 63, 54};
// 控制循环次数
for(int i = 0; i < arrays.length - 1;i++){
for(int j = 0; j < arrays.length - i -1; j++){
// 比较当前元素和后一个元素的大小,若当前元素大,则交换位置
if(arrays[j] > arrays[j+1]){
arrays[j] = arrays[j] + arrays[j+1];
arrays[j+1] = arrays[j] - arrays[j+1];
arrays[j] = arrays[j] - arrays[j+1];
}
}
}
System.out.println(Arrays.toString(arrays));
}
}
图示解析:
选择排序
选择排序原理:定义一个最小值(最大值)下标(假设第一个为最小值),遍历数组(内循环)进行比较,若定义下标对应元素小于后一个元素,交换他们的下标,进行下一轮比较,内层循环循环完找到最小值(最大值),交换位置。进行多轮循环(外层循环),使次序恢复升序(降序)
代码举例:
public class Demo{
public static void main(String[] args){
int[] arr = {2, 5, 8, 1, 4};
// 遍历数组
for(int i = 0; i < arr.length;i++){
// 定义最小值下标
int flag = i;
// 遍历数组元素
for(int j = i+1; j < arr.length; j++){
// 如果最小值大于后一个值,换下标,下标永远是最小值
if(arr[flag] > arr[j]){
flag = j;
}
}
// 避免重复,优化算法
if(flag!=i){
// 找到最小值后交换位置
arr[flag] = arr[flag] ^ arr[i];
arr[i] = arr[flag] ^ arr[i];
arr[flag] = arr[flag] ^ arr[i];
}
System.out.println(Arrays.toString(arr));
}
}
}
图例解析:此图只标注了外层循环和交换变化。
插入排序
插入排序原理:首先插入排序默认第一个元素是有序的,从第二个元素起遍历数组,定义当前值,定义当前值下标,然后与默认有序值进行比较,若当前值小于有序值,则有序值后移,之后交换位置,然后从下一个元素继续开始,往复循环实现全部有序。(个人理解)
代码示例:
public class Demo{
public static void main(String[]args){
int[] arr = {9, 5, 8, 1, 4};
// 遍历数组
for(int i = 1; i < arr.length; i++){
// 定义当前值
int flag = arr[i];
// 定义当前值下标
int flagIndex = i;
// 判断当前下标是否有效;当前值是否小于有序值
while(flagIndex > 0 && flag < arr[flagIndex -1]){
// 满足条件则当前值被有序值覆盖
arr[flagIndex] = arr[flagIndex - 1];
// 当前值下标前移,继续向前比较(满足条件的话)
flagIndex --;
}
if(flagIndex != i){
// 如果当前值的位置发生变化,则插入元素
arr[flagIndex]= flag;
}
}
System.out.println(Arrays.toString(arr));
}
}
图示解析:
快速排序
- 什么是快速排序:快速排序采用一种 分治的策略,类似于二分查找将无序的数组进行二分,使得左边为小于基准值,右边大于基准值,最后递归左右达到有序的一种排序方法,体现在快上面。
- 实现思路:首先定义一个基准,以及左右边界索引,然后在满足左索引小于右索引的情况下循环,然后开始从左右分别开始找,右边满足小于基准,左边寻找大于基准,然后交换位置,就完成了基本有序,然后在left==right的条件下交换基准和对应元素的位置。再进行左右两边的分别递归快排实现完全排序。
图示:
代码实现:
public static void sort(int[] arr, int start, int end) {
// 递归头,满足结束递归
if (start >= end) {
return;
}
int left = start;// 左边界
int right = end;// 右边界
int flag = arr[start];// 基准
int temp = 0; // 临时变量
while (left < right) {
// 从右边找,找大于基准的值
while (left < right && arr[right] >= flag) {
right--;
}
// 从左边找,找小于基准的值
while (left < right && arr[left] <= flag) {
left++;
}
if (left != right) {
temp = arr[left];
arr[left] = arr[right];
arr[right] = temp;
}
}
// 左右索引相等,进行基准交换
if (left == right) {
arr[start] = arr[left];
arr[left] = flag;
}
// 左右递归
sort(arr, start, left - 1);
sort(arr, left + 1, end);
}
快排的注意点:递归头得在程序开头,不然你会知道什么叫做数组下标越界;第二个,里面定义的变量比较多,要盯清变量的变化,不然你又会知道什么叫做栈溢出异常(StackOverflowException)
然后写代码最好这样写:
/*
* 快速排序:
* 首先定义基准,定义左右边界值
* 然后:1.右边小于基准,左边大于基准 交换位置,
* 2.右边往左移动,左边向右移动
* 直至左右满足左边小于基准,右边大于基准,交换基准位置
* 递归左右,实现排序*/
先写自然语言,然后用代码去一步步实现,这样你的思路永远安安全全。
总结
以上就是学习到的三个排序算法,就熟练度来说冒泡更优一点,因为接触的多熟悉一点可能也是最简单的原因吧。
经过测试这三个排序算法性能上最优的是插入排序,其次是冒泡排序,最后是选择排序,插入基本上比其余两个快将近二十倍,冒泡比选择快近四分之一的样子,以下是测试:当然快排没测,有快选快,无快插入啊!!
冒泡排序 | 选择排序 | 插入排序 |
---|---|---|
测试方法
Long start,end;
start = System.currentTimeMillis();
// 代码块
end = System.currentTimeMillis();
System.out.println("程序运行时间为:" + (end - start)+ "ms")
这段代码的作用是对程序从第一句开始到最后一句结束的运行时间,只能看个大概。