1、二分查找实现思路?
1:已有排序数组A(假设已经排好)
2:确定左右边界(L R),对应数组下标值
3: 获取中间索引m = Floor(L + R) / 2
4: 比较目标值t和中间值a[m]的大小,不断缩小边界,循环查找
a: a[m] == t 找到,直接返回中间值下标索引。
b: a[m] > t 中间值大于目标值,说明右侧的值都大于t,无需比较,中间索引左边去找
设置右边界为m - 1,重新查找。
c: a[m] < t 中间值小于目标值,说明左侧的值都小于t,无需比较,中间索引右边去找
设置左边界为m + 1,重新查找。
5:当L > R时,左边界大于右边界,说明没有找到,应结束循环
2、二分查找代码
代码如下(示例):
package com.xiang;
public class BinarySearch {
public static void main(String[] args) {
int[] array = {1,5,8,11,19,22,31,35,40,45,48,49,50};
int target = 48;
int idx = binarySearch(array, target);
System.out.println(idx);
}
//二分查找,找到返回元素索引,找不到返回-1
private static int binarySearch(int[] a, int t) {
//定义左边界,右边界,中间值索引
int l = 0, r = a.length - 1, m;
while(l <= r){
m = (l + r) / 2;//获取中间值索引
if(a[m] == t){
return m;
}else if(a[m] < t){
l = m + 1;
}else{
r = m - 1;
}
}
return -1;//没找到返回-1
}
}
3、冒泡排序思路?
1:将数组中相邻的两个元素进行比较,大的交换到后面,依次进行,直到最大值冒泡到最后面。
2:重复第1步,找到第二大的值,第三大的值......
4、冒泡排序代码
4.1 冒泡排序代码:
package day01.sort;
import java.util.Arrays;
public class BubbleSort {
public static void main(String[] args) {
int[] a = {5, 9, 7, 4, 1, 3, 2, 8};
bubble(a);
}
private static void bubble(int[] a) {
for (int j = 0; j < a.length - 1; j++) {
//一轮冒泡
for(int i = 0; i < a.length - 1; i++){
if(a[i] > a[i+1]){ //前一个元素大于后一个元素,交换位置
swap(a,i,i+1);
}
}
System.out.println("第"+j+"轮冒泡:"+Arrays.toString(a));
}
}
public static void swap(int[] a, int i, int j){
int t = a[i];
a[i] = a[j];
a[j] = t;
}
}
4.2 冒泡排序代码优化:
package day01.sort;
import java.util.Arrays;
public class BubbleSort {
public static void main(String[] args) {
int[] a = {5, 2, 7, 4, 1, 3, 8, 9};
// int[] a = {1, 2, 3, 4, 5, 7, 8, 9};
bubble_v2(a);
}
//最终优化版(综合 减少比较次数 和 冒泡次数)
public static void bubble_v2(int[] a) {
int n = a.length - 1; //n 表示下一次要比较的次数,初始比较次数为数组长度 - 1
while(true){
int last = 0;
for (int i = 0; i < n; i++) {
System.out.println("比较次数:" + i);
if(a[i] > a[i + 1]){
swap(a, i,i + 1);
last = i; //获取每一轮冒泡最后一次交换索引
}
}
n = last;
System.out.println("第轮冒泡:"
+ Arrays.toString(a));
if(n == 0){ //n等于0时,跳出外层循环
break;
}
}
}
//优化,减少比较次数 减少冒泡次数
public static void bubble(int[] a) {
for (int j = 0; j < a.length - 1; j++) {
// 一轮冒泡
boolean swapped = false; //是否发生了交换
for(int i = 0; i < a.length - 1 - j; i++){ // -j 减少比较次数
System.out.println("比较次数:" + i);
if(a[i] > a[i+1]){ //前一个元素大于后一个元素,交换位置
swap(a,i,i+1);
swapped = true;
}
}
System.out.println("第"+j+"轮冒泡:"+Arrays.toString(a));
// 一轮冒泡后没有发生交换,跳出循环
if(!swapped){
break;
}
}
}
public static void swap(int[] a, int i, int j){
int t = a[i];
a[i] = a[j];
a[j] = t;
}
}
5、选择排序
5.1 选择排序思路
1:将数组分为两个子集,已排序的和未排序的,每一轮从未排序的子集中选择最小的元素,放入 排序子集。
2:重复以上步骤,直到整个数组有序。
3:优化方式:为减少交换次数,每一轮先找到最小的索引,在每轮最后再进行交换。
5.2 选择排序代码
代码如下:
package day01.sort;
import java.util.Arrays;
public class SelectionSort {
public static void main(String[] args) {
int[] a = {5,3,7,2,1,9,8,4};
selection(a);
}
//选择排序
private static void selection(int[] a){
for (int i = 0; i < a.length - 1; i++) {
// i 代表每轮选择最小元素要交换到的目标索引
int s = i; // s 代表最小元素的索引,初始为0
for (int j = s + 1; j < a.length; j++) {
if(a[s] > a[j]){
s = j;
}
}
if(s != i){
swap(a,s,i);
}
System.out.println(Arrays.toString(a));
}
}
private static void swap(int[] a, int s, int i) {
int t = a[s];
a[s] = a[i];
a[i] = t;
}
}
5.3 冒泡排序与选择排序的比较
1:二者的平均时间复杂度都是O(n^2)
2:选择排序一般要快于冒泡,因为交换次数少
3:但如果集合有序度高,冒泡优于选择
4:冒泡属于稳定排序算法,选择属于不稳定排序算法
6、插入排序
6.1 插入排序思路
1:将数组分为两个区域,已排序区域和未排序区域,每一轮从未排序区域中取出第一个元素,插 入到排序区域(需保证顺序)
2:重复以上步骤,直到整个数组有序
3:优化方式
a. 待插入元素进行比较时,遇到比自己小的元素,就代表找到了插入位置,无需进行后续比较 b. 插入时可以直接移动元素,而不是交换元素
6.2 插入排序代码
代码如下:
package day01.sort;
import java.util.Arrays;
public class InsertSort {
public static void main(String[] args) {
int[] a = {9,3,7,2,5,1,4};
insert(a);
}
private static void insert(int[] a) {
// i 表示待插入元素的索引
for (int i = 1; i < a.length; i++) {
int t = a[i]; //保存待插入元素的值
int j = i - 1; //代表已排序区域的元素的索引
while (j >=0) {
if(t < a[j]){
a[j + 1] = a[j];
}else {
break; //退出循环,减少比较的次数
}
j--;
}
a[j + 1] = t;
System.out.println(Arrays.toString(a));
}
}
}
6.3 插入排序与选择排序的比较
1:二者的时间复杂度都是O(n^2)
2:大部分情况下,插入都略优于选择
3:有序集合插入时间复杂度为O(n)
4:插入属于稳定排序算法,而选择属于不稳定排序算法
6.4 希尔排序
7、快速排序
7.1 快速排序_文字描述
1. 每一轮选择一个基准点(pivot)进行分区
1. 让小于基准点的元素进入一个分区,大于基准点的元素进入另一个分区
2. 当分区完成的时候,基准点元素的位置就是其最终位置
2. 在子分区内重复以上过程,直至子分区元素个数少于或等于 1 ,分而治之的思想
7.2 快速排序_实现方式
1. 单边循环快排(lomuto洛穆托分区方案)
1. 选择最右元素作为基准点元素
2. j 指针负责找到比基准点小的元素,一旦找到则与 i 进行交换
3. i 指针维护小于基准点元素的边界,也是每次交换的目标索引
4. 最后基准点与 i 交换,i 即为分区位置
2. 双边循环快排(并不完全等价于 hoare 霍尔分区方案)
1. 选择最左元素作为基准点元素
2. j 指针负责从右向左找比基准点小的元素,i 负责从左向右找比基准点大的元素,一旦找到二 者交换,直到 i,j 相交为止
3. 最后基准点与 i (此时 i 和 j 相等) 交换,i 即为分区位置
3. 双边循环快排的几个要点
1. 基准点在左边,并且要先 j 后 i
2. while (i < j && a[j] > pv)j--
3. while (i < j && a[i] <= pv)i++
4. 块速排序_特点
1. 平均时间复杂度是O(nlog2n),最坏时间复杂度是O(n^2)
2. 数据较大时,优势非常明显
3. 属于不稳定排序
7.3 单边循环_快排代码
代码如下:
package day01.sort;
import java.util.Arrays;
//单边循环_快速排序
public class QuickSort1 {
public static void main(String[] args) {
int[] a = {5,3,7,2,9,8,1,4};
quick(a,0, a.length - 1);
}
//递归调用
public static void quick(int[] a, int l, int h){
//结束递归的条件
if(l >= h){
return;
}
int p = partition(a, l, h); // p 索引值
quick(a,l,p-1); //左边界分区范围确定
quick(a,p + 1,h); //右边界分区范围确定
}
//l 表示分区的左边界,h 表示分区的右边界
private static int partition(int[] a, int l, int h){
int pv = a[h];
int i = l;
for (int j = l; j < h; j++) {
if(a[j] < pv){ //找到比基准点小的元素,与 i 交换
if(j != i){
swap(a,j,i);
}
i++;
}
}
if(h != i){
swap(a,h,i);
}
System.out.println(Arrays.toString(a)+ "i=" + i);
//返回值代表了基准点元素所在的正确索引,用它确定下一轮分区的边界
return i;
}
private static void swap(int[] a, int j, int i) {
int t = a[i];
a[i] = a[j];
a[j] = t;
}
}
7.4 双边循环_快排代码
代码如下:
package day01.sort;
import java.util.Arrays;
//双边循环_快速排序
public class QuickSort2 {
public static void main(String[] args) {
int[] a = {5,3,7,2,9,8,1,4};
System.out.println(Arrays.toString(a));
quick(a,0, a.length - 1);
}
//递归调用
public static void quick(int[] a, int l, int h){
//结束递归的条件
if(l >= h){
return;
}
int p = partition(a, l, h); // p 索引值
quick(a,l,p-1); //左边界分区范围确定
quick(a,p + 1,h); //右边界分区范围确定
}
private static int partition(int[] a, int l, int h){
int pv = a[l];
int i = l;
int j = h;
while (i < j){
// j 从右找小的
while (i < j && a[j] > pv){
j--;
}
// i 从左找大的
while (i < j && a[i] <= pv){
i++;
}
swap(a,i,j);
}
swap(a,l,j); // 与基准点元素交换
System.out.println(Arrays.toString(a)+ "j=" + j);
return j;
}
private static void swap(int[] a, int i, int j) {
int t = a[i];
a[i] = a[j];
a[j] = t;
}
}