二分查找
循环遍历实现,重要的是了解循环不变量——通过在 [l...r] 这个范围中寻找target,这样就不会搞不清l和r如何赋值的边界条件了。
int binarySearch(int[] arr, int n, int target) {
int l = 0, r = n - 1; // 在[l...r]的范围里寻找target 【循环不变量 -- 有一个声明是不会发生变化的】
while(l <= r) {
int mid = l + ( r-l)/2;
if(target == arr[mid])
return mid;
if(target > arr[mid]){
l = mid + 1; // target 在 [mid+1...r] 中
}else{
r = mid - 1; // target 在 [l...mid-1] 中
}
}
return -1; // 没有找到
}
// 思考: 如果在 [l...r)范围中寻找target,应该怎么修改代码呢?
二分查找比较难想的题目: 33. 搜索旋转排序数组
moveZeros
体会[0...k) 前闭后开范围的用法,nums[k++]=nums[i]。循环不变量为[0...k)始终为非零元素,k初始值为0,该范围开始没有非零元素。
// [0,1,2,0,5] --> [1,2,5,0,0]
void moveZeros(int[] nums){
// nums中,[0...k)的元素均为非零元素
int k = 0; // [0...k)为空
// 遍历到第i个元素后,保证[0...i]中所有非0元素
// 都按照顺序排列在[0...k)中
// 同时,[i...k]为0
for(int i=0; i<nums.size(); i++){
if(nums[i]!=0){
if(i==k) // 优化代码,防止不必要的交换
k++; continue;
swap(nums, i, k++);
}
}
}
滑动窗口
数组中一个窗口,通过移动左右边界始终满足某个条件。
leetcode例题:
3 最长不重复子串
209 最少和大于s的数
438 find all anagrams in a string
76 minimum window substring
// 209 Minimum Size Subarray Sum
int minSubArrayLen(int s, int[] nums){
int lo = 0;
int hi = -1; // [lo..hi]
int min = nums.length+1;
int sum = 0;
while (hi+1 < nums.length) { // [2,5,7,12] 10
if (sum < s) {
hi++;
sum += nums[hi];
}
while (lo < nums.length && sum >= s) {
min = Math.min(min, hi - lo + 1);
sum -= nums[lo++];
}
}
return min==nums.length+1?0:min;
}
// 3 Longest Substring Without Repeating Characters
public int lengthOfLongestSubstring(String s){
char[] chs = s.toCharArray();
HashSet<Character> set = new HashSet<>();
int lo = 0;
int hi = -1;
int max = 0;
while (hi + 1 < chs.length && lo < chs.length) {
if (!set.contains(chs[hi + 1])) {
hi++;
set.add(chs[hi]);
max = Math.max(max, hi - lo + 1);
} else {
set.remove(chs[lo--]);
}
}
快速排序
int[] quickSort(int[] nums){
if(nums==null || nums.length=1) return nums;
quickSort_r(nums, 0, nums.length-1);
return nums;
}
void quickSort_r(int[] nums, int left, int right){
if(left>=right) return;
int idx = partition(nums, left, right);
quickSort_r(nums, left, idx-1);
quickSort_r(nums, idx+1, right);
}
// 1路快排
// 3 1 4 6 7 5
int partition_1(int[] nums, int left, int right){
// [left,i)<pivot
int pivot = nums[right];
int i=left;
for(int j=left, j<right, j++){
if(nums[j]<pivot){
swap(nums, i, j);
i++;
}
}
swap(nums, i, right);
return i;
}
// 2路快排
void partition_2(int[] nums, int left, int right){
// [left,i)<pivot [i,j]-处理中 (j,right]>pivot
int pivot = nums[right];
int i=left,j=right-1;
while(i<=j){
if(nums[i]>pivot){
swap(nums,i,j);
j--; // i不变,交换后i的位置可能还会>pivot
}else{
i++;
}
}
swap(nums,i,right);
return i;
}
三路快排
func quickSort(nums []int) []int {
quickSort_r(nums, 0, len(nums)-1)
return nums
}
func quickSort_r(nums []int, l int, r int) {
if nums == nil || len(nums) == 1 {
return
}
low, high := partition(nums, l, r)
if low > l {
quickSort_r(nums, l, low-1)
}
if high < r{
quickSort_r(nums, high+1, r)
}
}
// 1,1,1,2,2,0,0,2,1
// 三路快排,优化重复值
func partition_3(nums []int, l int, r int) (int, int) {
// l,low)<pivot [low,high]==pivot (high,r]>pivot
pivot := nums[r]
low, high := l, r
for i := l; i <= high; {
if nums[i] < pivot {
swap(nums, i, low)
low++
i++
} else if nums[i] > pivot {
swap(nums, i, high)
high--
} else {
i++
}
}
return low, high
}
func swap(nums []int, i, j int) {
tmp := nums[i]
nums[i] = nums[j]
nums[j] = tmp
}
无序数组选择最小的k个数
大小为k的大顶堆,堆顶是k个数里面最大的,将数组中的元素一次推入,只要比堆顶元素小的,就将堆顶元素出队
PriorityQueue<Freq> Java库是小顶堆实现的优先队列。
int parent(int index) (index-1) / 2
int leftChild(int index) 2*index+1
int rightChild(int index) 2*index+2
堆排序: 最关键的接口:void heapify(int[] arr, int n, int k) ;
// 从第K个节点开始siftdown到数组末尾
// 2k+1 2k+2 (n-1)/2
private void heapify(int[] arr, int n, int k){
while(2*k+1<n){
int j = 2*k+1;
if(j+1<n && arr[j+1]>arr[j]){
j = j+1;
}
if(arr[k]>arr[j]) break;
swap(arr, k, j);
k = j;
}
}
// 递归
private void heapify(int[] arr, int n, int k){
if(k>=n) return;
int j = k;
if(2*k+1<n && arr[2*k+1]>arr[j]) j=2*k+1;
if(2*k+2<n && arr[2*k+2]>arr[j]) j=2*k+2;
if(j!=k){
swap(arr, j, k);
heapify(arr, n, j);
}
}
// 从小到大排序 大顶堆
void heapSort(int arr){
int n = arr.length;
// 堆化
for(int i=(n-2)/2; i>=0; i--){
heapify(arr, n, i);
}
// 原地交换
for(int i=n-1; i>0; i--){
swap(arr, 0, i);
heapify(arr, i, 0);
}
}
跳跃游戏