最近找工作,手撸一些基础的算法。写个博客顺便复习一下。
ps:有的算法不详细的讲步骤,不懂的算法请移步其他博客。
/*
冒泡排序 稳定 时O(n2) 空O(1)
*/
public static void bubbleSort(int arr[]){
//第一层循环确定遍历次数,每一层都可以将最大的一个数交换到数组最末尾
for (int i = 1; i < arr.length; i++) {
//声明一个布尔变量,数组有序的情况下结束排序,优化时间
boolean n = false;
for (int j = 0; j < arr.length - i; j++) {
if(arr[j] >= arr[j + 1]){
int temp = arr[j+1];
arr[j+1] = arr[j];
arr[j] = temp;
n = true;
}
}
if(!n) break;
}
}
/*
选择排序 不稳定 时O(n2) 空O(1)
*/
public static void selectSort(int[] arr,int p){
//设置p为递归的标记位从0开始,min为初始值,遍历一次数组找出最小值,下标用q记录
if(p == arr.length-1) return;
int min = arr[p];
int q = -1;
for (int i = p+1; i < arr.length; i++) {
if(arr[i] < min){
min = arr[i];
q = i;
}
}
//若找出最小值,将其交换至递归标志位
if(q != -1){
int temp = arr[p];
arr[p] = arr[q];
arr[q] = temp;
}
selectSort(arr,p+1);
}
/*
快速排序 不稳定 时O(n2)/O(nlogn) 空O(nlogn)
*/
public static void QuickSort(int arr[],int left,int right){
//设置每次递归的数组第一个数为标志位,设置两个指针分别指向除了标志位的最左和最右
//循环:先从最右边往左找到比标志位小的,再从最左往右找比标志位大的,找到之后交换,直到两个指针碰在一起,循环结束。
//最后将标志位与指针碰撞的位置交换
//递归标志位两边的数组
if(left>=right) return;
int i = left;
int j = right;
int k = arr[left];
while(i=k){
j--;
}
while(i
/*
归并排序 稳定 时O(nlogn) 空O(n) 二分+归并 logn+n
*/
public static void Merge(int[] arr){
Sort(arr,0,arr.length-1);
}
private static void Sort(int[] arr, int left, int right) {
if(right - left < 1) return;
int mid = (right + left)/2;
//先将数组切分成最小的单元素
Sort(arr,left,mid);
Sort(arr,mid+1,right);
merge(arr,left,mid,right);
}
private static void merge(int[] arr, int left, int mid, int right) {
//将两个部分数组合在一起
int[] Arr = new int[right-left+1];
int l = left;
int r = mid + 1;
int i = 0;
//先依据大小进行合并到临时数组直到其中一个部分数组合并完成
while(l<=mid && r<=right){
if(arr[l] <= arr[r]){
Arr[i] = arr[l];
i++;
l++;
}else {
Arr[i] = arr[r];
i++;
r++;
}
}
//再将另一个数组放进临时数组里
if(l>mid){
while(r<=right){
Arr[i] = arr[r];
i++;
r++;
}
}else {
while (l<=mid){
Arr[i] = arr[l];
i++;
l++;
}
}
i = 0;
//最后将临时数组覆盖到原数组
for (int j = left; j <=right ; j++) {
arr[j] = Arr[i];
i++;
}
}
/*
堆排序 不稳定 时O(nlogn) 空O(1)
*/
public static void heapSort(int[] arr){
int i = arr.length;
while(i>=2){
//先找出最后一个非叶节点p
int p = i/2;
//p向前遍历,从右孩子往左孩子遍历
//注意:每一次遍历结束后 i-- 这样就不用遍历排序好的节点了
for(;p>0;p--){
int l = 2*p;
int r = 2*p+1;
if(r<=i && arr[r-1]>arr[p-1]){
int temp = arr[p-1];
arr[p-1] = arr[r-1];
arr[r-1] = temp;
}
if(l<=i && arr[l-1]>arr[p-1]){
int temp = arr[p-1];
arr[p-1] = arr[l-1];
arr[l-1] = temp;
}
}
int temp = arr[i-1];
arr[i-1] = arr[0];
arr[0] = temp;
i--;
}
}
/*
插入排序 稳定 时O(n2) 空O(1)
*/
public static void insertSort(int[] arr){
for (int i = 1; i < arr.length; i++) {
//每次循环找出未排序的第一个数在已排序数组中的位置p
int p = -1;
for (int j = 0; j < i; j++) {
if(arr[i] <= arr[j]){
p = j;
break;
}
}
if(p != -1){
int temp = arr[i];
//将p位置往后的已排序数组后移一位,再将该数放入p位置
for (int j = i-1; j >=p; j--) {
arr[j+1] = arr[j];
}
arr[p] = temp;
}
}
}
/*
基数排序 稳定 时O(logRB) 空O(n) R:基数 B:真数0-9
*/
public static void radixSort(int[] arr){
//求基数R
int R = -1;
for (int i = 0; i < arr.length; i++) {
int count = 0;
int num = arr[i];
while(num !=0){
num = num/10;
count++;
}
R = R>count?R:count;
}
//声明一个队列(“桶”)为元素的,容量为10链,用于存储每次循环得到的中间数
List> l = new LinkedList<>();
for (int i = 0; i < 10; i++) {
Queue q = new LinkedList<>();
l.add(q);
}
for (int i = 0; i < R; i++) {
for (int j = 0; j < arr.length; j++) {
int r = getFigure(arr[j],i);
l.get(r).offer(arr[j]);
}
int p = 0;
//将链中的元素依次取出,进行下一次循环
for (int j = 0; j < arr.length; j++) {
while(!(l.get(j).isEmpty())){
arr[p] = l.get(j).poll();
p++;
}
}
}
}
//获取i第k位的数
public static int getFigure(int i,int k)
{
int[] a = {1,10,100,1000,10000,100000,1000000,10000000,100000000,1000000000};
return (i/a[k])%10;
}
这是我见过最简洁的希尔排序,也很容易懂。
该部分转自:https://blog.csdn.net/qq_33054265/article/details/82747330
/*
shell排序 稳定 时O() 空O(1)
*/
public static void shellSort(int[] a) {
int N = a.length;
//希尔增量h
for(int h = N / 2; h > 0; h /= 2){
for(int i = h; i < N; i++){
for(int j = i; j >= h && a[j] < a[j - h]; j -= h){
int temp = a[j];
a[j] = a[j-h];
a[j-h] = temp;
}
}
}
}