双指针是一种常用的算法思想,通常用于在数组或链表等数据结构中解决一些问题。双指针技巧通过使用两个指针来遍历数据结构,从而在一次遍历中解决问题,避免使用额外的数据结构,从而降低时间复杂度和空间复杂度。
快慢指针一般用于解决链表相关的问题。快指针每次移动两步,慢指针每次移动一步。通过快慢指针的遍历,可以找到链表的中间节点、判断链表是否有环、寻找链表倒数第 k 个节点等问题。
左右指针一般用于解决数组或字符串相关的问题。左指针从数组的最左边开始,右指针从数组的最右边开始,然后向中间移动。通过左右指针的遍历,可以找到满足某种条件的子数组或子字符串,比如两数之和、反转数组、滑动窗口等问题。
该函数使用了双指针技巧来移除数组中等于给定值 val 的元素。双指针方法可以将数组中所有等于 val 的元素移动到数组的末尾,并返回新数组的长度。
在最坏情况下,如果数组中所有元素都等于 val,那么需要遍历整个数组,时间复杂度是 O ( n ) O(n) O(n),其中 n 是数组的长度。
该函数只使用了常量级别的额外空间来存储变量和常数。没有使用与输入规模成比例的额外数据结构。因此,空间复杂度是 O ( 1 ) O(1) O(1)。
func removeElement(nums []int, val int) int {
length := len(nums)
left, right := 0, length-1
for left<=right {
if nums[left]==val && nums[right]!= val {
nums[left], nums[right] = nums[right], nums[left]
left++
right--
}
if nums[left] != val {
left++
}
if nums[right] == val {
right--
}
}
return left
}
fast指针用于遍历数组,slow指针用于标记新数组的边界。当fast指针找到一个不等于val的元素时,将其复制到slow指针所在的位置,并递增slow指针。这样,slow指针之前的元素就构成了新数组。
func removeElement(nums []int, val int) int {
length := len(nums)
slow, fast := 0, 0
for ; fast<length; fast++ {
if nums[fast] != val {
nums[slow] = nums[fast]
slow++
}
}
return slow
}
func removeDuplicates(nums []int) int {
arrlen := len(nums)
slow, fast := 0, 1
for ; fast<arrlen; fast++ {
if nums[fast] != nums[slow] {
slow++
nums[slow] = nums[fast]
}
}
return slow+1
}
题目链接:LeetCode-80. 删除有序数组中的重复项 II
func removeDuplicates(nums []int) int {
length := len(nums)
if length == 0 {
return 0
}
slow, fast := 0, 0
count := 0
pre := nums[fast]-1
for ; fast<length; fast++ {
if nums[fast] == pre {
count++
} else {
count=0
}
if count <= 1 {
nums[slow] = nums[fast]
pre = nums[slow]
slow++
}
}
return slow
}
func reverse(arr []int, left int, right int) {
length := len(arr)
if length == 0 || length == 1 || left>=right || left<0 {
return
}
for ; left<=right; left,right=left+1,right-1 {
arr[left], arr[right] = arr[right], arr[left]
}
}
func rotate(nums []int, k int) {
length := len(nums)
k = k%length
reverse(nums, 0, length-1)
reverse(nums, 0, k-1)
reverse(nums, k, length-1)
}
该函数使用了一个字符串切片 strArr 来存储结果,其长度不会超过输入数组 nums 的长度。在最坏情况下,整个数组都是独立的连续整数序列,结果中每个整数都需要一个独立的字符串表示。所以空间复杂度是 O ( n ) O(n) O(n)。
import (
"fmt"
)
func summaryRanges(nums []int) []string {
length := len(nums)
strArr := make([]string, 0)
slow, fast := 0, 0
str := ""
for ; fast < length; fast++ {
if fast==length-1 || nums[fast]+1 != nums[fast+1] {
if nums[fast] == nums[slow] {
str = fmt.Sprintf("%d", nums[fast])
} else {
str = fmt.Sprintf("%d->%d", nums[slow], nums[fast])
}
strArr = append(strArr, str)
slow = fast+1
}
}
return strArr
}
题目链接:LeetCode-剑指 Offer 05. 替换空格
该函数使用了常量级别的额外空间来存储变量和常数,以及一个大小为 2 * spaceNum 的临时字节切片 tmp 来存储替换空格后多出来的字符。空间复杂度随着输入字符串 s 中空格的数量线性增长。因此,空间复杂度是 O ( s p a c e N u m ) O(spaceNum) O(spaceNum),其中 spaceNum 是输入字符串 s 中空格的数量。
func replaceSpace(s string) string {
slice := []byte(s)
length := len(slice)
spaceNum := 0
for _, v := range slice {
if v == ' ' {
spaceNum++
}
}
tmp := make([]byte, spaceNum*2)
slice = append(slice, tmp ...)
slow, fast := len(slice)-1, length-1
for fast >= 0 && slow >= 0 {
if slice[fast] == ' ' {
slice[slow] = '0'
slice[slow-1] = '2'
slice[slow-2] = '%'
slow = slow-3
fast--
} else {
slice[slow] = slice[fast]
slow--
fast--
}
}
return string(slice)
}
func sortColors(nums []int) {
length := len(nums)
if length == 0 {
return
}
left, right, i := 0, length-1, 0
for i<=right {
if nums[i] == 0 {
nums[left], nums[i] = nums[i], nums[left]
left++
i++
} else if nums[i] == 2 {
nums[right], nums[i] = nums[i], nums[right]
right--
} else {
i++
}
}
}