关于算法与数据结构的整理1.0

时间:2021-07-09 01:02:56   收藏:0   阅读:30

位操作

& 与运算

两个位上都为1时,结果才为1

| 或运算

两个位上都为0时,结果才为0

异或运算(重要)

相同为0,不同为1
无进位相加
0 ^ N = N; N ^ N = 0;异或满足交换律、结合律

~ 取反运算

0变1 1变0


二分法

注意点:
mid = (L + R)/2 (L + R)有可能会溢出
上式写成mid = L + (R - L)/2 ————>mid = L + (R - L)>>1


十大排序算法

https://www.cnblogs.com/guoyaohua/p/8600214.html

技术分享图片

选择排序

思想概述
每次循环溜一圈,把遍历到较小值的下标保存,遍历到尾值后,将保存的下标最小值与最左边的值(未排序过的)交换
每次找到先记录位置,遍历完一圈后再交换
双层循环

public static void selectionSort(int[] arr) {
		if (arr == null || arr.length < 2) {
			return;
		}
		for (int i = 0; i < arr.length - 1; i++) {
			int minIndex = i;
			for (int j = i + 1; j < arr.length; j++) {
				minIndex = arr[j] < arr[minIndex] ? j : minIndex;
			}
			swap(arr, i, minIndex);
		}
	}

	public static void swap(int[] arr, int i, int j) {
		int tmp = arr[i];
		arr[i] = arr[j];
		arr[j] = tmp;
	}

冒泡排序

思想概述
每次把最大值的冒泡到最右边
每当遍历到较大的就交换

public static void bubbleSort(int[] arr) {
		if (arr == null || arr.length < 2) {
			return;
		}
		for (int e = arr.length - 1; e > 0; e--) {
			for (int i = 0; i < e; i++) {
				if (arr[i] > arr[i + 1]) {
					swap(arr, i, i + 1);
				}
			}
		}
	}

	public static void swap(int[] arr, int i, int j) {
		arr[i] = arr[i] ^ arr[j];
		arr[j] = arr[i] ^ arr[j];
		arr[i] = arr[i] ^ arr[j];
	}

插入排序(打扑克)

从前向后遍历,依次做到当前位置上向前数的数全都有序
把最小的排到前面

public static void insertionSort(int[] arr) {
		if (arr == null || arr.length < 2) {
			return;
		}
                //i = 1:0~0已经有序
                //使0~i有序
		for (int i = 1; i < arr.length; i++) {//使0~i做到有序
			for (int j = i - 1; j >= 0 && arr[j] > arr[j + 1]; j--) {  //每次排到i位置,i之前的位置上的数已经有序
				swap(arr, j, j + 1);
			}
		}
	}
        //i和j是一个位置的话,会出错
	public static void swap(int[] arr, int i, int j) {
		arr[i] = arr[i] ^ arr[j];
		arr[j] = arr[i] ^ arr[j];
		arr[i] = arr[i] ^ arr[j];
	}

归并排序

思路:递归 + 二分

技术分享图片

public static void mergeSort(int[] arr) {
		if (arr == null || arr.length < 2) {
			return;
		}
		mergeSort(arr, 0, arr.length - 1);
	}

	public static void mergeSort(int[] arr, int l, int r) {
		if (l == r) {
			return;
		}
		int mid = l + ((r - l) >> 1);
		mergeSort(arr, l, mid);
		mergeSort(arr, mid + 1, r);
		merge(arr, l, mid, r);
	}

	public static void merge(int[] arr, int l, int m, int r) {
		int[] help = new int[r - l + 1];
		int i = 0;
		int p1 = l;
		int p2 = m + 1;
		while (p1 <= m && p2 <= r) {    //  p1和p2都没有越界
			help[i++] = arr[p1] < arr[p2] ? arr[p1++] : arr[p2++];
		}
		while (p1 <= m) {              //  p1没有越界、p2越界
			help[i++] = arr[p1++];
		}
		while (p2 <= r) {              //  p2没有越界、p1越界
			help[i++] = arr[p2++];
		}
		for (i = 0; i < help.length; i++) {
			arr[l + i] = help[i];
		}
	}

注意点
int a[3] = {0, 1, 2};
int i = 0;
a[i++] = a[2]//数组变成{2, 1, 2}显然在这里是先进行a[i] = a[2]的赋值操作,再对i进行加一操作。
a[++i] = a[2]//数组变成{0, 2, 2}显然在这里是先进行i加一操作,再进行a[i] = a[2]的赋值操作。

归并排序需要借助额外空间

技术分享图片


关于快排的算法题

技术分享图片

技术分享图片

——————————————————————————————————————————————————————————
——————————————————————————————————————————————————————————

技术分享图片

快速排序

思想:
不改进的快速排序
1)把数组范围中的最后一个数作为划分值,然后把数组通过荷兰国旗问题分成三个部
分:
左侧<划分值、中间==划分值、右侧>划分值
2)对左侧范围和右侧范围,递归执行
分析
1)划分值越靠近两侧,复杂度越高;划分值越靠近中间,复杂度越低
2)可以轻而易举的举出最差的例子,所以不改进的快速排序时间复杂度为O(N^2)

随机快速排序(改进的快速排序)
1)在数组范围中,等概率随机选一个数作为划分值,然后把数组通过荷兰国旗问题分
成三个部分:
左侧<划分值、中间==划分值、右侧>划分值
2)对左侧范围和右侧范围,递归执行
3)时间复杂度为O(N*logN)

public static void quickSort(int[] arr) {
		if (arr == null || arr.length < 2) {
			return;
		}
		quickSort(arr, 0, arr.length - 1);
	}

	public static void quickSort(int[] arr, int l, int r) {
		if (l < r) {
			swap(arr, l + (int) (Math.random() * (r - l + 1)), r);
			int[] p = partition(arr, l, r);
			quickSort(arr, l, p[0] - 1);// <区域 
			quickSort(arr, p[1] + 1, r);// >区域
		}
	}
        
        //这是处理一个arr[l..r]的函数
        //默认以arr[r]做划分,arr[r]-> p <p == p >p
        //返回等于区域(左边界、右边界),所以返回一个长度为2的数组res,res[0] res[1]
	public static int[] partition(int[] arr, int l, int r) {
		int less = l - 1;    // <区右边界
		int more = r;        // >区左边界
		while (l < more) {   // l表示当前数 
			if (arr[l] < arr[r]) {
				swap(arr, ++less, l++);
			} else if (arr[l] > arr[r]) {
				swap(arr, --more, l);
			} else {
				l++;
			}
		}
		swap(arr, more, r);
		return new int[] { less + 1, more };
	}

	public static void swap(int[] arr, int i, int j) {
		int tmp = arr[i];
		arr[i] = arr[j];
		arr[j] = tmp;
	}

堆结构及堆排序

1,堆结构就是用数组实现的完全二叉树结构
2,完全二叉树中如果每棵子树的最大值都在顶部就是大根堆
3,完全二叉树中如果每棵子树的最小值都在顶部就是小根堆
4,堆结构的heapInsert与heapify操作
5,堆结构的增大和减少
6,优先级队列结构,就是堆结构

堆排序
1,先让整个数组都变成大根堆结构,建立堆的过程:
1)从上到下的方法,时间复杂度为O(NlogN)
2)从下到上的方法,时间复杂度为O(N)
2,把堆的最大值和堆末尾的值交换,然后减少堆的大小之后,再去调
整堆,一直周而复始,时间复杂度为O(N
logN)
3,堆的大小减小成0之后,排序完成

public static void heapSort(int[] arr) {
		if (arr == null || arr.length < 2) {
			return;
		}
		for (int i = 0; i < arr.length; i++) {
			heapInsert(arr, i);
		}
		int size = arr.length;
		swap(arr, 0, --size);
		while (size > 0) {
			heapify(arr, 0, size);
			swap(arr, 0, --size);
		}
	}

	public static void heapInsert(int[] arr, int index) {
		while (arr[index] > arr[(index - 1) / 2]) {
			swap(arr, index, (index - 1) /2);
			index = (index - 1)/2 ;
		}
	}

	public static void heapify(int[] arr, int index, int size) {  //堆化
		int left = index * 2 + 1;
		while (left < size) {
			int largest = left + 1 < size && arr[left + 1] > arr[left] ? left + 1 : left;
			largest = arr[largest] > arr[index] ? largest : index;
			if (largest == index) {
				break;
			}
			swap(arr, largest, index);
			index = largest;
			left = index * 2 + 1;
		}
	}

	public static void swap(int[] arr, int i, int j) {
		int tmp = arr[i];
		arr[i] = arr[j];
		arr[j] = tmp;
	}

桶排序

不基于比较的排序,基于数据状况的排序

桶排序思想下的排序
1)计数排序
2)基数排序: 从个位一直到最高位排序,依次进桶 出桶
分析:
1)桶排序思想下的排序都是不基于比较的排序
2)时间复杂度为O(N),额外空间负载度O(M)
3)应用范围有限,需要样本的数据状况满足桶的划分

技术分享图片

// only for no-negative value
	public static void radixSort(int[] arr) {
		if (arr == null || arr.length < 2) {
			return;
		}
		radixSort(arr, 0, arr.length - 1, maxbits(arr));
	}

	public static int maxbits(int[] arr) {	//统计最大值的十进制位数
		int max = Integer.MIN_VALUE;
		for (int i = 0; i < arr.length; i++) {
			max = Math.max(max, arr[i]);
		}
		int res = 0;
		while (max != 0) {
			res++;
			max /= 10;
		}
		return res;
	}

	public static void radixSort(int[] arr, int begin, int end, int digit) {
		final int radix = 10;
		int i = 0, j = 0;
		//有多少个数准备多少个辅助空间
		int[] bucket = new int[end - begin + 1];
		for (int d = 1; d <= digit; d++) {	//有多少位就进出几次 digit
			//10个空间
			//cont[0] 当前位(d位)是0的数字有多少个
			//cont[1] 当前位(d位)是(0和1)的数字有多少个
			//cont[0] 当前位(d位)是(0、1和2)的数字有多少个
			//cont[i] 当前位(d位)是(0~i)的数字有多少个

			int[] count = new int[radix];
			for (i = begin; i <= end; i++) {
				j = getDigit(arr[i], d);
				count[j]++;
			}
			for (i = 1; i < radix; i++) {
				count[i] = count[i] + count[i - 1];
			}
			for (i = end; i >= begin; i--) {
				j = getDigit(arr[i], d);
				bucket[count[j] - 1] = arr[i];
				count[j]--;
			}
			for (i = begin, j = 0; i <= end; i++, j++) {
				arr[i] = bucket[j];
			}
		}
	}

总结

排序算法的稳定性及其汇总
同样值的个体之间,如果不因为排序而改变相对次序,就是这个排序是有稳定性的;否则就没有。
不具备稳定性的排序:
选择排序、快速排序、堆排序
具备稳定性的排序:
冒泡排序、插入排序、归并排序、一切桶排序思想下的排序
目前没有找到时间复杂度O(N*logN),额外空间复杂度O(1),又稳定的排序。

常见的坑
1,归并排序的额外空间复杂度可以变成O(1),但是非常难,不需要掌握,有兴趣可以搜“归并排序 内部缓存法”
2,“原地归并排序”的帖子都是垃圾,会让归并排序的时间复杂度变成O(N^2)
3,快速排序可以做到稳定性问题,但是非常难,不需要掌握, 可以搜“01stable sort”
4,所有的改进都不重要,因为目前没有找到时间复杂度O(N*logN),额外空间复杂度O(1),又稳定的排序。
5,有一道题目,是奇数放在数组左边,偶数放在数组右边,还要求原始的相对次序不变,碰到这个问题,可以怼面试官。

技术分享图片

原文:https://www.cnblogs.com/BigMonster-S/p/14988178.html

评论(0
© 2014 bubuko.com 版权所有 - 联系我们:wmxa8@hotmail.com
打开技术之扣,分享程序人生!