今天主要说一下常见的算法 ,于是我百度了一下点进那 “ 牛逼 ” 的知乎看了一眼 ,完蛋了 ,这都是些神马 ??? 我怎么一个都不会呢 ,我要的可是那种很常见的算法啊 ,好吧 ,无形中又被深深的伤了一刀 ,好在我迅速调节状态 。管他呢 ,我就按照自己 low 的来吧 。进入正题 ,我要说的几种算法就是二分法查找 ,冒泡排序和选择排序 。以数组为例 ,谈谈它们在 Java 中的实现 。
二分法查找 ,举个例子说明一下 ,我们在玩猜数字游戏的时候经常会用到 。小明给出数的范围 ,0 ~ 100 ,我们猜 50 ,小明回答说 “ 大了 ” ,那我们会接着猜 25 ,就这样我们一次次的逼近了真相 。这就是二分法的思想 。也可以说是分治的思想 ,分而治之 ,逐一攻破 。我们同样也可以将这种思想应用到有序数组的查找上 。我们可以拿数组中间的数和待查找元素进行比较 ,其过程类似与猜数字 ,变得就是 “ 中间 ” 位的那个数 。最终我们可以找到目标元素 。方法实现 :
private static int binarySearch(int[] list,int target) {
int low = 0;
int high = list.length - 1;
//直到low>high时还没找到关键字就结束查找,返回-1
while(low<=high){
int mid = (low+high) / 2;
if(target < list[mid]){
high = mid - 1;
}
else if(target > list[mid]){
low = mid + 1;
}
else if(target == list[mid]){
return mid;
}
}
return -1;
}
冒泡排序 ,想象一下鱼儿在水中吐了一个泡 ,在向上的过程中 ,是不是在越变越大 。其实冒泡排序的思想也就是这样的 。给你 n 个数 ,我们用第一个数和第二个数进行比较 ,大的那个就放第二位 ,然后第二位和第三位比较 ,大的那个放在第三位 ,依次比下去 ,待到和最后一个比较结束 ,我们就能得到这 n 个数中最大的那个 ,且放在最后一个位置上 。( 这要是不明白就回头再读一遍 ) 这样一轮比较结束我们得到一个最大的数 ,第二轮我们再进行比较 ,同理 ,可以得到剩余 n – 1 个数中最大的数 。且放在倒数第二位 ,这就就是内层 for 循环条件 – 1 的原因 ,因为没必要和上一轮的最后一个数进行比较嘛 。方法实现 :
public static void bubbleSort(int[] arr) {
/* * 外面的for循环决定一个长度为 n 的数据要比较多少轮才能完成排序。 * 里面的for循环决定每次一轮循环中要做多少次才能结束。 */
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
}
}
}
}
PS . 只需要换一个符号 ,就可以得到从大到小的序列 。
快速排序 ,快速排序是对冒泡排序的一种改进 。它的基本思想是将要排序的数据分割成独立的两部分 ,其中一部分的所有数据都比另外一部分的所有数据都要小 。然后在按照此方法对两部分数据分别进行快速排序 ,最终得到一个有序的序列 。需要说明的是 ,快速排序的平均时间复杂度是 O(nlogn) 。但是时间复杂度还是 O(n^2) ,因为时间复杂度是按照最坏结果来计算的 。为了方便理解 ,使用一组数据模拟一下排序的过程 。
假设要排的数组为:int[] a = { 5 2 8 9 2 3 4 9 };
选择 key = 5, 开始时 i = 0,j = 7
下标 0 1 2 3 4 5 6 7
开始 5 2 8 9 2 3 4 9
i j
第一次找 5 2 8 9 2 3 4 9
i j
交换: 5 2 4 9 2 3 8 9
i j
第二次找 5 2 4 9 2 3 8 9
i j
交换: 5 2 4 3 2 9 8 9
i j
第三次找 5 2 4 3 2 9 8 9
ij
调整key: 2 5 4 3 5 9 8 9
ij
方法实现 :
private static void quickSort(int[] a, int low, int high) {
//找到递归算法的出口
if( low > high) {
return;
}
int i = low;
int j = high;
//默认 key
int key = a[low];
//开始一趟排序
while( i < j) {
//先从右往左找到第一个小于 key 的数 ,
//这么做是因为在与 key 值交换的时候,保证了交换的数小于现有的 key 值
//若是大于的话,j 指针就会继续向左移动 。
while(i<j && a[j] > key){
j--;
}
//从左往右找到第一个大于等于 key 的数
while( i<j && a[i] <= key) {
i++;
}
//交换,达到以 key “分治” 的效果
if(i<j) {
int temp = a[i];
a[i] = a[j];
a[j] = temp;
}
}
// 当 i = j 时,调整 key 的值为 a[i] == a[j]
int temp = a[i];
a[i] = a[low];
a[low] = temp;
//对 key 左边的数快速排序
quickSort(a, low, i-1 );
//对 key 右边的数快速排序
quickSort(a, i+1, high);
}
后记 :不得不说 ,这些简单的算法我都没有很熟悉 ,只能是那种原理清楚 ,但是实现起来却是磕磕绊绊的状态 。还有 ,以上给出的实现是可以进一步优化的 ,记得之前在面试的时候被问到过冒泡排序的问题 ,现场也被问到过如何优化 。