二分查找法
时间复杂度:O(log n)
- 题目:在一个一维且有序的数组中,查找数组中是否有 flag 的数存在。
- 首先判断数组中间值是否大于目标值,如果大于,则搜索前半部分数组。如果小于则搜索后半部分数组。
- 在判断半部分的数组的中间值是否大于目标值,来判断接下来的数组搜索的范围。知道找到该值
- 循环跳出条件:数组的最小索引值大于数组的最大索引值。跳出还没有找到,则该数组没有该值。
代码实现:
public class search {
public static int bin_search(int[] array,int key){
int right = array.length;
int left = 0;
int mid = 0;
while(left <= right){
mid = (left + right)/2;
if(array[mid] == key){
return mid;
}else if(array[mid] > key){
right = mid-1;
}else if (array[mid] < key){
left = mid+1;
}
}
return -1;
}
public static void main(String[] args) {
int[] array = {1,2,3,4,5,6,7,8,10,15};
System.out.println(bin_search(array, 12));
}
}
插入排序
用下面一张图来进行演示插入排序,序列为(6 5 3 1 8 7 2 4)(图片来自:流丶觞)
- 我们默认第一个数字是有序的即 6。
- 然后看第二个数字 5 ,5 比 6 小,所以 5 应该插在 6 的前面,该序列变成了 5 6 3 1 8 7 2 4;
- 然后看第三个数字 3, 3 比 5 小,所以应该插在 5 的前面,序列变成了 3 5 6 1 8 7 2 4;
- 依次将后面的数字进行插入。
代码实现:
public static void insertSort(int[] array){
for(int i = 1;i < array.length;i++){
//从第二个数字开始进行插入
int temp = array[i];
int j ;
//依次将插入位置之后的数据向后移动
for(j = i;j > 0 && array[j-1] > temp;j--){
array[j] = array[j-1];
}
array[j] = temp;
}
}
希尔排序
希尔排序又称缩小增量法是对直接插入排序的优化。序列(9 1 2 5 7 4 8 6 3 5)分组的表达式为(gap = gap/2,可以为其他的表达式)。
- 第一趟:先选定一个初始组数,将待排序序列分成个组,将距离为相同的数字分为一组,如(9和4)(1和8)(2和6)(5和3)(7和5)进行组内排序。排序过后为(4 1 2 3 5 9 8 6 5 7)
- 第二趟:将选定的组数进行折半为2组,在进行以上分组排序,两组(4 2 5 8 5)(1 3 6 7 9),组内进行插入排序
- 第三趟:上面分两组,接下来是分1组,就是第二趟结束后的序列,进行插入排序。
代码实现:
public static void shellSort(int[] array){
int gap = array.length;
while(true){
gap = gap / 2;
shellWithGap(array,gap);
if(gap == 1){
return ;
}
}
}
public static void shellWithGap(int[] array,int gap){
for(int i = 0;i < array.length - gap;i++){
int k = array[i + gap];
int j;
for(j = i;j >= 0 && array[j] > k;j -= gap){
array[j + gap] = array[j];
}
array[j + gap] = k;
}
}
选择排序
- 每次从待排序中的数据中选择一个最大的或者最小的数字,存放在序列的其实位置,知道全部排序完成。
- 在元素集合array[i]–array[n-1]中选择关键码最大(小)的数据元素
- 若它不是这组元素中的最后一个(第一个)元素,则将它与这组元素中的最后一个(第一个)元素交换
- 在剩余的array[i]–array[n-2](array[i+1]–array[n-1])集合中,重复上述步骤,直到集合剩余1个元素
代码实现:
public static void selectSort(int[] array){
for(int i = 0; i < array.length - 1; i++){
int min = i;
for(int j = i + 1;j < array.length; j++){
if(array [j] < array[min]){
min = j;
}
}
int temp = array[min];
array[min] = array[i];
array[i] = temp;
}
}
升级版本的插入排序。
public static void selsceSortVersion2(int[] array){
int low = 0;
int hight = array.length -1;
while(low < hight){
int min = low;
int max = low;
for(int i = low + 1;i <= hight;i++){
if(array[i] > array[max]){
max = i;
}if(array[i] > array[min]){
min = i;
}
}
swap(array,min,low);
if(max == low){
max = min;
}
swap(array,max,hight);
low++;
hight--;
}
}
private static void swap (int[] array,int m,int n){
int temp = array[m];
array[m] = array[n];
array[n] = temp;
}
堆排序
- 堆排序(Heapsort)是指利用堆积树(堆)这种数据结构所设计的一种排序算法,它是选择排序的一种。它是 通过堆来进行选择数据。需要注意的是排升序要建大堆,排降序建小堆。
private static void swap(int[]array,int n,int m){
int temp = array[n];
array[n] = array[m];
array[m] = temp;
}
public static void heapSort(int[] array) {
creatHeap(array, array.length);
for (int i = 0; i < array.length - 1; i++) {
// 无序 [0, array.length - i)
swap(array, 0, array.length - i - 1);
// 无序 [0, array.length - i - 1)
// 无序区间的长度: array.length - i - 1
heapsfy(array, array.length - i - 1, 0);
}
}
private static void heapsfy(int[]array,int size,int k){
while(true){
int left = k*2+1;
//判断是否为叶子结点,
// 若为叶子结点,则直接返回
if(left >size){
return;
}
int max = left;
if(left + 1 < size){
if(array[max] < array[left+1]){
max = left + 1;
}
if(array[k] < array[max]){
swap(array,max,k);
k = max;
}
}
}
}
冒泡排序
- 冒泡排序的实质是:在排序的过程中,相邻的元素通过比较来进行不断的交换。
- 举例子:一个序列为:4 9 7 2 5 1 3(排序为递增序列)
- 首先 4 和 9 比较,4 比 9 小,不交换;9 比 7 大 交换 (交换后的序列为:4 7 9 2 5 1 3);接下来是 9 和 2 比较,9 比 2 大所以交换(交换为的序列为: 4 7 2 9 5 1 3);下来就是 9 和 5 比较,9 和 1 比较 9 和 3 比较(第一轮交换完毕,最后的序列为:4 7 2 5 1 3 9)。
- 第一轮交换完 之后进行第二轮交换,方法相同。最后的结果为(4 2 5 1 3 7 9)。
- 同理,第三轮结果( 2 4 1 3 5 7 9);第四轮(2 1 3 4 5 7 9);第五轮(1 2 3 4 5 7 9);第六轮(1 2 3 4 5 7 9)。第七轮(1 2 3 4 5 7 9)
- 举的例子比较特殊,可以看出在第五轮的时候,该序列就已经成为有序数列了,但是后面的比较交换还是会执行。
- 优化:我们可以看出,第一轮通过比较可以确定了9 的位置,第二轮确定了 7 的位置。依次确定该序列中所有元素的位置。所以,在新的一轮比较中,确定好的位置就可以不用进行比较了,来节省时间。在最后一轮(第七轮)可以不进行比较,因为一共七个数字,六个数字都确定好了位置,最后一个最后一个位置当然是哪个没有确定的数字。
代码实现:
public static void sort(int[] array){
int length = array.length;
for(int i = 0;i < length - 1;i++){
for (int j = 0;j < length - 1 - i ;j++){
if(array[j] > array[j + 1]) {
int temp = array[j];
array[j] = array[j + 1];
array[j + 1] = temp;
}
}
}
}
//打印比较过程:
//4 7 2 5 1 3 9
//4 2 5 1 3 7 9
//2 4 1 3 5 7 9
//2 1 3 4 5 7 9
//1 2 3 4 5 7 9
//1 2 3 4 5 7 9
快速排序
- 快速排序其实就是将选择序列中的一个数,作为基准点,然后将小于基准点的数字放在左面,大于基准点的数字放在右面。
- 然后在小于基准点的序列中在找一个基准点,在进行小的放左边,大的放右边。右边大于基准点的数字也是一样的。
- 如此反复,一直到序列数字为1为止。此时就是有序的序列了。
假设待排序的序列为(6 1 2 7 9 3 4 5 10 8);
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
---|---|---|---|---|---|---|---|---|---|
6 | 1 | 2 | 7 | 9 | 3 | 4 | 5 | 10 | 8 |
i | j |
- 我们选择6为基准点,此时j进行判断操作,j 现在所在位置的数字是8所以我们需要向前挪动(–操作)直到找到比6小的数字,就是5(此时j = 7)。
- 然后 i 进行向前进行移动(++ 操作),找到比6大的数字 ,找到 7(此时 i = 3)。
- 再将 5 和 7 进行交换(i = 3 和 j = 7),第一轮交换结束。此时的序列为(6 1 2 5 4 3 9 7 10 8)
- 第二轮交换开始,先 j 进行寻找比 6 小的数字 (找到4,j = 6),在 i 进行寻找比6大的数字(找到9,i=4),在此进行交换。第二轮交换完毕。(6 1 2 5 4 3 9 7 10 8)
- 第二轮交换完之后,j 进行寻找比 6 小的数字 (找到3,j = 5),i 进行++操作,寻找比6 大的数字,但是在进行++ 操作时(此时i = 5),i 和 j 在同一位置(相遇),说明 i 和 j 的寻找操作结束,此时基准数 6 和 3 进行交换,交换后的序列为(3 1 2 5 4 6 9 7 10 8) 。
- 将6作为分界点,左边为比6 小的数字,右边为比 6 大的数字。分为两个序列,在进行上面所述的操作。
整个过程的交换图:
可以看看下面的动态图进行,进一步的理解(图片来自:Angus_lxy)
代码实现:
//快速排序
public static void quick_sort(int[] array,int left,int right) {
int i, j, t, temp;
if (left > right) {
return;
}
temp = array[left];
i = left;
j = right;
while (i != j) {
while (array[j] >= temp && i < j) {
j--;
}
while (array[i] <= temp && i < j) {
i++;
}
if (i < j) {
t = array[i];
array[i] = array[j];
array[j] = temp;
}
}
array[left] = array[i];
array[i] = temp;
quick_sort(array,left,i-1);
quick_sort(array,i + 1, right);
}
归并排序
归并排序用一句话来讲就是将已有序的序列合并,得到完全有序的序列。即先将每个子序列有序,再使每个子序列段间有序。如果是两个有序序列合并,则是二路归并,还有更多的多路归并。
动态图演示:
代码实现:
//递归实现
public static void mergeSort(int[] array) {
mergeSortInternal(array, 0, array.length);
}
private static void mergeSortInternal(int[] array, int low, int high) {
if (low + 1 >= high) {
return;
}
int mid = (low + high) / 2;
// [low, mid)
// [mid, high)
mergeSortInternal(array, low, mid);
mergeSortInternal(array, mid, high);
merge(array, low, mid, high);
}
private static void merge(int[] array, int low, int mid, int high) {
int length = high - low;
int[] extra = new int[length];
// [low, mid)
// [mid, high)
int iLeft = low;
int iRight = mid;
int iExtra = 0;
while (iLeft < mid && iRight < high) {
if (array[iLeft] <= array[iRight]) {
extra[iExtra++] = array[iLeft++];
} else {
extra[iExtra++] = array[iRight++];
}
}
while (iLeft < mid) {
extra[iExtra++] = array[iLeft++];
}
while (iRight < high) {
extra[iExtra++] = array[iRight++];
}
for (int i = 0; i < length; i++) {
array[low + i] = extra[i];
}
}
//非递归实现
public static void mergeSortNoR(int[] array) {
for (int i = 1; i < array.length; i = i * 2) {
for (int j = 0; j < array.length; j = j + 2 * i) {
int low = j;
int mid = j + i;
int high = mid + i;
if (mid >= array.length) {
continue;
}
if (high > array.length) {
high = array.length;
}
merge(array, low, mid, high);
}
}
}