学习内容
学习文档:数组理论基础
收获总结
-
快速排序:
快速排序是一种基于分治法的排序算法。首先,选择一个基准元素,然后通过分区操作将数组划分为两部分,一部分元素小于基准值,另一部分元素大于基准值。递归地对这两部分进行排序,最终将数组排序完成。
package main import "fmt" func quickSort(arr []int, low, high int) { if low < high { pi := partition(arr, low, high) quickSort(arr, low, pi-1) quickSort(arr, pi+1, high) } } func partition(arr []int, low, high int) int { pivot := arr[high] i := low - 1 for j := low; j < high; j++ { if arr[j] < pivot { i++ arr[i], arr[j] = arr[j], arr[i] } } arr[i+1], arr[high] = arr[high], arr[i+1] return i + 1 } -
二分查找:
二分查找是一种在有序数组中查找目标值的算法。它通过不断将数组分成两半,并与中间元素进行比较,从而快速缩小查找范围,最终确定目标值的位置。如果目标值不存在,则返回
-1。package main import "fmt" func binarySearch(arr []int, low, high, target int) int { if low <= high { mid := low + (high-low)/2 if arr[mid] == target { return mid } else if arr[mid] > target { return binarySearch(arr, low, mid-1, target) } else { return binarySearch(arr, mid+1, high, target) } } return -1 } -
双指针,对撞指针,快慢指针常用初始化设计:
-
双指针:可以从数组两端开始,常用于查找两个数的和问题。初始化
i为数组起始位置,j为数组结束位置,当i < j时迭代。另一种情况是从同一端开始,适用于查找具有特定差值的元素对。 -
快慢指针:快慢指针常用于处理链表或数组中的问题,比如删除重复元素。
i和j初始化为数组或链表的起始位置,迭代条件是j指针不越界。
// 双指针 - 从两端开始 func twoSum(arr []int, target int) []int { i, j := 0, len(arr)-1 for i < j { sum := arr[i] + arr[j] if sum == target { return []int{i, j} } else if sum < target { i++ } else { j-- } } return nil } // 双指针 - 从同一端开始 func findPairWithDiff(arr []int, diff int) []int { i, j := 0, 1 for i < len(arr) && j < len(arr) { if i != j && arr[j]-arr[i] == diff { return []int{i, j} } else if arr[j]-arr[i] < diff { j++ } else { i++ } } return nil } // 快慢指针 func removeDuplicates(arr []int) int { i, j := 0, 1 for j < len(arr) { if arr[i] != arr[j] { i++ arr[i] = arr[j] } j++ } return i + 1 } -
题目解析
题目1:704. 二分查找
-
题目描述: 给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1。
-
示例:
输入: nums = [-1,0,3,5,9,12], target = 9 输出: 4 -
解法总结: 使用二分查找方法。初始化左右指针
low和high,每次计算中间元素并与目标值进行比较,更新指针范围,直到找到目标值或确认不存在。 -
代码实现:
func search(nums []int, target int) int { low := 0 high := len(nums) - 1 for low <= high { mid := low + (high-low)/2 if target == nums[mid] { return mid } else if target > nums[mid] { low = mid + 1 } else { high = mid - 1 } } return -1 }时间复杂度: O(log n),因为每次查找都将搜索范围缩小一半。 空间复杂度: O(1),只使用了常数空间。
题目2:27. 移除元素
-
题目描述: 给你一个数组 nums 和一个值 val,你需要原地移除所有数值等于 val 的元素,返回剩余元素的数量。元素的顺序可能发生改变。
-
示例:
输入: nums = [3,2,2,3], val = 3 输出: 2, nums = [2,2] -
解法总结: 使用双指针方法。
i和j都从数组的头部开始遍历,当j指向的元素不等于val时,将其赋值给i指向的位置,并递增i,最终返回i的值作为新数组的长度。 -
代码实现:
func removeElement(nums []int, val int) int { i, j := 0, 0 count := 0 for j <= len(nums)-1 { if nums[j] != val { nums[i] = nums[j] count++ i++ } j++ } return count }时间复杂度: O(n),遍历数组一次即可完成操作。 空间复杂度: O(1),只使用了常数空间。
题目3:977.有序数组的平方
-
题目描述: 给你一个按非递减顺序排序的整数数组 nums,返回每个数字的平方组成的新数组,要求也按非递减顺序排序。
-
示例:
输入: nums = [-4,-1,0,3,10] 输出: [0,1,9,16,100] -
解法总结: 可以采用两种方法。一种是先对所有元素平方后使用快速排序,另一种是使用双指针从数组两端向中间遍历,将较大的平方值放在结果数组的末尾。第二种方法要注意,因为给定的数组是非递减排序的,所以平方值最大的元素一定在数组的两端(中间小),所以当我们遍历需要for循环从后往前遍历!
-
代码实现:
方法一:先平方后排序
func sortedSquares(nums []int) []int { for i := 0; i < len(nums); i++ { nums[i] = nums[i] * nums[i] } quickSort(nums, 0, len(nums)-1) return nums } func quickSort(arr []int, low, high int) { if low < high { pivot := partition(arr, low, high) quickSort(arr, low, pivot-1) quickSort(arr, pivot+1, high) } } func partition(arr []int, low, high int) int { pivotValue := arr[high] i := low - 1 for j := low; j < high; j++ { if arr[j] < pivotValue { i++ arr[i], arr[j] = arr[j], arr[i] } } arr[i+1], arr[high] = arr[high], arr[i+1] return i + 1 }时间复杂度: O(n log n),因为对所有元素进行平方操作后,再使用快速排序。 空间复杂度: O(log n),递归调用栈的空间开销。
方法二:双指针法
func sortedSquares(nums []int) []int { i, j := 0, len(nums)-1 result := make([]int, len(nums)) for h := len(nums) - 1; h >= 0; h-- { if nums[i]*nums[i] > nums[j]*nums[j] { result[h] = nums[i] * nums[i] i++ } else { result[h] = nums[j] * nums[j] j-- } } return result }时间复杂度: O(n),因为只需要一次遍历即可完成操作。 空间复杂度: O(n),因为使用了额外的数组来存储结果。