选择排序:
public static void selectionSort(int[] arr){
if(arr == null || arr.length < 2){
return;
}
for(int i = 0; i < arr.length; i++){
int minIndex = i;
for(int j = i+1; j < arr.length; j++){
minIndex = arr[i] < arr[j]?j:minIndex;
}
swap(arr, i, minIndex);
}
}
public static void swap(int[] arr, int i, int j){
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
冒泡排序:
public static void bubbleSort(int[] arr){
if(arr == null || arr.length <2){
return;
}
for(int e = arr.length - 1; e > 0; e--) { // 0~e
for(int i = 0; i< e; i++) {
if(arr[i] > arr[i+1]){
swap(arr,i,i+1);
}
}
}
}
//交换arr的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];
}
异或运算:相同为0,不同为1(可以理解为无进位相加)
异或运算不是对内存里的元素值进行运算,而是对元素的内存地址进行运算
异或相关面试题:
一个整型数组中,已知只有一种数出现奇数次,其余所有数出现偶数次
问题1:找到该奇数次的数
int eor = 0 ; eor ^ 数组中所有的数,得到eor该数
public static void printOddTimesNum1(int[] arr){
int eor = 0;
for(int i = 0; i < arr.length; i++){
eor ^= arr[i];
}
System.out.println(eor);
}
问题2:如果有两种数出现奇数次,其余所有数出现偶数次,找出出现奇数次的两个数
提取一个数最右边的1的方式是 int rightOne = eor & (~eor +1);
public static void printOddTimesNum2(int[] arr){
int eor = 0;
for(int i = 0; i < arr.length; i++){
eor ^= arr[i];
}
// eor = a^b;
// eor != 0
// eor必然有一个位置上是1
int rightOne = eor ^ (~eor + 1); // 提取出一个数最右边的1
int onlyOne = 0;
for(int cur : arr){
if((cur & rightOne) == 0){
onlyOne ^= cur;
}
}
System.out.println(onlyOne + " " + (eor^onlyOne));
}
插入排序:
public static void insertSort(int[] arr){
if(arr == null || arr.length < 2){
return;
}
// 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--){
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];
}
二分法:
问题1:在一个有序数组中,找某个数是否存在
问题2:在有序数组中,找 >=某个数最左侧的位置
问题3:局部最小值问题 : arr中,无序,相邻数一定不相等
public int binarySearch(int[] nums, int target) {
int left = 0;
int right = nums.length - 1; // 注意
while(left <= right) {
int mid = left + (right - left) / 2;
if(nums[mid] == target)
return mid;
else if (nums[mid] < target)
left = mid + 1; // 注意
else if (nums[mid] > target)
right = mid - 1; // 注意
}
return -1;
}
递归排序:
问题:用递归方法找一个数组中的最大值
public static int getMax(int[] arr) {
return process(arr,0,arr.length-1);
}
// arr[L...R]范围上求最大值 N
public static int process(int[] arr,int L, int R) {
if (L == R) { // arr[L...R]范围伤只有一个数,直接返回
return arr[L];
}
int mid = L + ((R-L) >> 1); // L+(R-L)/2
int leftMax = process(arr,L,mid);
int rightMax = process(arr,mid-1,R);
return Math.max(leftMax,rightMax);
}
归并排序:额外空间复杂度n级别
public static void mergeSort(int[] arr) {
if(arr == null || arr.length < 2){
return ;
}
process(arr,0,arr.length-1);
}
public static void process(int[] arr, int L, int R){
if(L == R){
return;
}
int mid = L +((R-L) >> 1);
process(arr,L,mid);
process(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){
help[i++] = arr[p1] <= arr[p2]?arr[p1++]:arr[p2++];
}
while(p1 <= M){
help[i++] = arr[p1++];
}
while(p2 <= R){
help[i++] = arr[p2++];
}
for(i = 0; i < help.length;i++){
arr[L + i] = help[i];
}
}
问题1:小和问题
在一个数组中,每一个数的左边比当前数小的数累加起来,叫做这个数组的小和。求一个数组的小和。例子:[1,3,4,2,5] 1左边比1小的数,没有;3左边比3小的数,1;4左边比4小的数。1,3;2左边比2小的数,1;5左边比5小的数,1、2、3、4;所以小和就是:1+1+3+1+1+3+4+2 = 16
划等为---------》求右边有多少个数比他大,1右边有4个数比他大1*4;3右边有2个比他大3*2;4右边有1个比他大4*1;2右边有1个比他大2*1;小和:4+6+4+2 = 16
相等时先拷贝右组
public static int smallSum(int[] arr){
if(arr == null || arr.length < 2){
return -1;
}
return 0;
}
// arr[L...R]既要排好序,也要产生小和
public int process(int[] arr, int L, int R){
if(L == R) {
return 0;
}
int mid = L + ((R-L) >> 1);
return process(arr,L,mid)
+ process(arr,mid+1,R)
+merge(arr,L,mid,R);
}
public int merge(int[] arr, int L,int mid, int R){
int[] help = new int[R-L+1];
int i = 0;
int p1 = L;
int p2 = mid+1;
int ret = 0;
// 相等时先拷贝右组
while(p1<=mid && p2 <= R){
ret += arr[p1] < arr[p2]?(R-p2+1)*arr[p1]:0;
help[i++] = arr[p1]<arr[p2]?arr[p1++]:arr[p2++];
}
while(p1 <= mid){
help[i++] = arr[p1++];
}
while(p2 <= R){
help[i++] = arr[p2++];
}
for (i = 0; i<help.length;i++){
arr[L+i] = help[i];
}
return ret;
}
问题2:逆序对问题
在一个数组中,左边的数如果比右边的数大,则这两个数构成逆序对,请打印所有逆序对。
Leetcode 剑指 Offer 51. 数组中的逆序对 求逆序对个数
while (p1 <= mid && p2 <= r) {
res += nums[p1] > nums[p2] ?(r - p2 + 1): 0 ;
help[i++] = nums[p1]>nums[p2]?nums[p1++]:nums[p2++];
}
快速排序:0(N*logN) 额外空间复杂度logN级别
荷兰国旗问题:
问题1:给定一个数组arr,和一个数num,请把小于等于num的数放在数组的左边,大于num的数放在数组的右边。要求额外空间复杂度0(1),时间复杂度0(N)
思路:1,arr[i] <= num,arr [i] 和<=区的下一个数交换,<=区右扩 i++;
2,arr[i] > num, i++
问题2:
在给定一个数组arr,和一个数num,请把小于num的数放在数组的左边,等于num的数放在数组的中间,大于num的数放在数组的右边。要求额外空间复杂度0(1),时间复杂度0(N)
思路:1,arr[i] < num, arr[i] 和<区的下一个数交换,<区右扩 i++;
2,arr[i] == num, i++
3,arr[i] > num, arr[i] 和>区的下一个数交换,>区左扩 ,i原地不动;
另附代码(Leetcode 75):
public void sortColors(int[] nums) {
if(nums == null || nums.length < 2){
return;
}
quickSort(nums,0,nums.length-1);
}
// arr[l...r]排好序
private static void quickSort(int[] arr, int l, int r) {
if(l < r) {
// 划分值等于区域的左右边界
int[] p = partition(arr,l,r);
quickSort(arr,l,p[0]-1); // <区
quickSort(arr,p[1]+1,r); // > 区
}
}
// 以区域内的最后一个数划分 > = < 最后与<区域的第一个交换
private static int[] partition(int[] arr, int l, int r) {
int less = l-1; // <区右边界
int more = r; // >区左边界
while (l < more){ // l表示当前数的位置arr[r] -> 划分值
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 temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}