Kun

Kun

IT学徒、技术民工、斜杠青年,机器人爱好者、摄影爱好 PS、PR、LR、达芬奇潜在学习者


共 279 篇文章


目录

  算法题第三篇 数组

数组相关算法

排序算法

十种排序算法

https://www.cnblogs.com/onepixel/articles/7674659.html

冒泡排序, 复杂度O(n2)

function bubbleSort(arr) {
    var len = arr.length;
    for (var i = 0; i < len - 1; i++) {
        for (var j = 0; j < len - 1 - i; j++) {
            if (arr[j] > arr[j+1]) {        // 相邻元素两两对比
                var temp = arr[j+1];        // 元素交换
                arr[j+1] = arr[j];
                arr[j] = temp;
            }
        }
    }
    return arr;
}

选择排序

开启一个循环,每轮从未排序区间选择最小的元素,将其放到已排序区间的末尾。

复杂度O(n2)

function selectionSort(arr) {
    var len = arr.length;
    var minIndex, temp;
    for (var i = 0; i < len - 1; i++) {
        minIndex = i;
        for (var j = i + 1; j < len; j++) {
            if (arr[j] < arr[minIndex]) {     // 寻找最小的数
                minIndex = j;                 // 将最小数的索引保存
            }
        }
        temp = arr[i];
        arr[i] = arr[minIndex];
        arr[minIndex] = temp;
    }
    return arr;
} 

插入排序

插入排序的工作原理与手动整理一副牌的过程非常相似。时间复杂度O(n2)

具体来说,我们在未排序区间选择一个基准元素,将该元素与其左侧已排序区间的元素逐一比较大小,并将该元素插入到正确的位置

工作流程:

  1. 初始状态下,数组的第 1 个元素已完成排序。
  2. 选取数组的第 2 个元素作为 base ,将其插入到正确位置后,数组的前 2 个元素已排序
  3. 选取第 3 个元素作为 base ,将其插入到正确位置后,数组的前 3 个元素已排序
  4. 以此类推,在最后一轮中,选取最后一个元素作为 base ,将其插入到正确位置后,所有元素均已排序
function insertionSort(arr) {
    var len = arr.length;
    var preIndex, current;
    for (var i = 1; i < len; i++) {
        preIndex = i - 1;
        current = arr[i];
        while (preIndex >= 0 && arr[preIndex] > current) {
            arr[preIndex + 1] = arr[preIndex];
            preIndex--;
        }
        arr[preIndex + 1] = current;
    }
    return arr;
}

希尔排序

function shellSort(arr) {
    var len = arr.length;
    for (var gap = Math.floor(len / 2); gap > 0; gap = Math.floor(gap / 2)) {
        // 注意:这里和动图演示的不一样,动图是分组执行,实际操作是多个分组交替执行
        for (var i = gap; i < len; i++) {
            var j = i;
            var current = arr[i];
            while (j - gap >= 0 && current < arr[j - gap]) {
                 arr[j] = arr[j - gap];
                 j = j - gap; 
            }
            arr[j] = current;
        }
    }
    return arr;
}

归并排序

function mergeSort(arr) {
    var len = arr.length;
    if (len < 2) {
        return arr;
    }
    var middle = Math.floor(len / 2),
        left = arr.slice(0, middle),
        right = arr.slice(middle);
    return merge(mergeSort(left), mergeSort(right));
}

function merge(left, right) {
    var result = [];

    while (left.length>0 && right.length>0) {
        if (left[0] <= right[0]) {
            result.push(left.shift());
        } else {
            result.push(right.shift());
        }
    }

    while (left.length)
        result.push(left.shift());

    while (right.length)
        result.push(right.shift());

    return result;
}

快速排序

选择一个基准数,通过一趟排序将要排序的数据分割成独立的两部分;其中一部分的所有数据都比另外一部分的所有数据都要小。然后,再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列

快速排序的基本思想:通过一趟排序将待排记录分隔成独立的两部分,其中一部分记录的关键字均比另一部分的关键字小,则可分别对这两部分记录继续进行排序,以达到整个序列有序。

function quickSort(arr, left, right) {
    var len = arr.length,
        partitionIndex,
        left = typeof left != 'number' ? 0 : left,
        right = typeof right != 'number' ? len - 1 : right;

    if (left < right) {
        partitionIndex = partition(arr, left, right);
        quickSort(arr, left, partitionIndex-1);
        quickSort(arr, partitionIndex+1, right);
    }
    return arr;
}

function partition(arr, left ,right) {     // 分区操作
    var pivot = left,                      // 设定基准值(pivot)
        index = pivot + 1;
    for (var i = index; i <= right; i++) {
        if (arr[i] < arr[pivot]) {
            swap(arr, i, index);
            index++;
        }        
    }
    swap(arr, pivot, index - 1);
    return index-1;
}

function swap(arr, i, j) {
    var temp = arr[i];
    arr[i] = arr[j];
    arr[j] = temp;
}

堆排序

  1. 输入数组并建立小顶堆,此时最小元素位于堆顶。
  2. 不断执行出堆操作,依次记录出堆元素,即可得到从小到大排序的序列
var len;    // 因为声明的多个函数都需要数据长度,所以把len设置成为全局变量

function buildMaxHeap(arr) {   // 建立大顶堆
    len = arr.length;
    for (var i = Math.floor(len/2); i >= 0; i--) {
        heapify(arr, i);
    }
}

function heapify(arr, i) {     // 堆调整
    var left = 2 * i + 1,
        right = 2 * i + 2,
        largest = i;

    if (left < len && arr[left] > arr[largest]) {
        largest = left;
    }

    if (right < len && arr[right] > arr[largest]) {
        largest = right;
    }

    if (largest != i) {
        swap(arr, i, largest);
        heapify(arr, largest);
    }
}

function swap(arr, i, j) {
    var temp = arr[i];
    arr[i] = arr[j];
    arr[j] = temp;
}

function heapSort(arr) {
    buildMaxHeap(arr);

    for (var i = arr.length - 1; i > 0; i--) {
        swap(arr, 0, i);
        len--;
        heapify(arr, 0);
    }
    return arr;
}

桶排序

桶排序通过设置一些具有大小顺序的桶,每个桶对应一个数据范围,将数据平均分配到各个桶中;然后,在每个桶内部分别执行排序;最终按照桶的顺序将所有数据合并。

function bucketSort(arr, bucketSize) {
    if (arr.length === 0) {
      return arr;
    }

    var i;
    var minValue = arr[0];
    var maxValue = arr[0];
    for (i = 1; i < arr.length; i++) {
      if (arr[i] < minValue) {
          minValue = arr[i];                // 输入数据的最小值
      } else if (arr[i] > maxValue) {
          maxValue = arr[i];                // 输入数据的最大值
      }
    }

    // 桶的初始化
    var DEFAULT_BUCKET_SIZE = 5;            // 设置桶的默认数量为5
    bucketSize = bucketSize || DEFAULT_BUCKET_SIZE;
    var bucketCount = Math.floor((maxValue - minValue) / bucketSize) + 1;   
    var buckets = new Array(bucketCount);
    for (i = 0; i < buckets.length; i++) {
        buckets[i] = [];
    }

    // 利用映射函数将数据分配到各个桶中
    for (i = 0; i < arr.length; i++) {
        buckets[Math.floor((arr[i] - minValue) / bucketSize)].push(arr[i]);
    }

    arr.length = 0;
    for (i = 0; i < buckets.length; i++) {
        insertionSort(buckets[i]);                      // 对每个桶进行排序,这里使用了插入排序
        for (var j = 0; j < buckets[i].length; j++) {
            arr.push(buckets[i][j]);                      
        }
    }

    return arr;
}

计数排序

function countingSort(arr, maxValue) {
    var bucket = new Array(maxValue + 1),
        sortedIndex = 0;
        arrLen = arr.length,
        bucketLen = maxValue + 1;

    for (var i = 0; i < arrLen; i++) {
        if (!bucket[arr[i]]) {
            bucket[arr[i]] = 0;
        }
        bucket[arr[i]]++;
    }

    for (var j = 0; j < bucketLen; j++) {
        while(bucket[j] > 0) {
            arr[sortedIndex++] = j;
            bucket[j]--;
        }
    }

    return arr;
}

基数排序

基数排序是按照低位先排序,然后收集;再按照高位排序,然后再收集;依次类推,直到最高位。有时候有些属性是有优先级顺序的,先按低优先级排序,再按高优先级排序。最后的次序就是高优先级高的在前,高优先级相同的低优先级高的在前。

var counter = [];
function radixSort(arr, maxDigit) {
    var mod = 10;
    var dev = 1;
    for (var i = 0; i < maxDigit; i++, dev *= 10, mod *= 10) {
        for(var j = 0; j < arr.length; j++) {
            var bucket = parseInt((arr[j] % mod) / dev);
            if(counter[bucket]==null) {
                counter[bucket] = [];
            }
            counter[bucket].push(arr[j]);
        }
        var pos = 0;
        for(var j = 0; j < counter.length; j++) {
            var value = null;
            if(counter[j]!=null) {
                while ((value = counter[j].shift()) != null) {
                      arr[pos++] = value;
                }
          }
        }
    }
    return arr;
}

无序数组

调整数组顺序使得奇数位于偶数前面(剑指offer21)

设置两个指针,偶数就删除插入尾部,奇数不做处理

class Solution:
    def exchange(self, nums: List[int]) -> List[int]:
        l=0
        h=len(nums)
        while l < h:
            if nums[l] % 2 == 0:
                nums.append(nums.pop(l))
                h-=1
            else:
                l+=1
        return nums

移动零 283

给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。

如,输入:[0,1,0,3,12]

输出:[1,3,12,0,0]

注意:不拷贝额外的数组,减少操作次数

class Solution:
    def moveZeroes(self, nums: List[int]) -> None:
        i = 0
        for num in nums:
            if num != 0:
                nums[i] = num
                i += 1
        for j in xrange(i, len(nums)):
            nums[j] = 0

两数之和(1)

好一点的实现

遍历生成hash表,反向map就好

const twoSum = function (nums, target) {
    // 解题思路:从无序数组中找到两个值,查找表方法记录,使两个值之和等于target
    /**
     * 查找表
     * @type {Map}
     */
    let m = new Map(),
    const len = nums.length;
    for (let i = 0; i < len; i++) {
        if (m.has(nums[i])) {
            return [m.get(nums[i]), i]
        }
        m.set((target - nums[i]), i)
    }
};

扑克牌中的顺子(剑指offer61)

若干副扑克牌中随机抽 5 张牌,判断是不是一个顺子,即这5张牌是不是连续的。2~10为数字本身,A为1,J为11,Q为12,K为13,而大、小王为0 ,可以看成任意数字。A 不能视为14。

【输入】 [1,2,3,4,5]
【输出】 True

【输入】 [0,0,1,2,5]
【输出】 True

js

var isStraight = function (nums) {
  let numberOfZero = 0;
  nums.sort((a, b) => a - b);
  let numsCount = 5;
  for (let i = 0; i < numsCount - 1; i++) {
    if (nums[i] === 0) {
      numberOfZero++;
    } else if (nums[i] === nums[i + 1]) {
      return false;
    }
  }
  return nums[4] - nums[numberOfZero] < 5;
};

三数之和(15)

给你一个包含n个整数的数组nums,判断nums中是否存在三个元素,使得这三个整数相加为0

例:输入:nums = [-1,0,1,2,-1,-4]

输出:[-1,-1,2],[-1,0,1]

js版本

题目中说明可能会出现多组结果,所以我们要考虑好去重

  • 1.为了方便去重,我们首先将数组排序
  • 2.对数组进行遍历,取当前遍历的数nums[i]为一个基准数,遍历数后面的数组为寻找数组
  • 3.在寻找数组中设定两个起点,最左侧的left(i+1)和最右侧的right(length-1)
  • 4.判断nums[i] + nums[left] + nums[right]是否等于0,如果等于0,加入结果,并分别将leftright移动一位
  • 5.如果结果大于0,将right向左移动一位,向结果逼近
  • 5.如果结果小于0,将left向右移动一位,向结果逼近
    var threeSum = function (nums) {
      const result = [];
      nums.sort((a, b) => a - b);
      for (let i = 0; i < nums.length; i++) {
        // 跳过重复数字
        if (i && nums[i] === nums[i - 1]) { continue; }
        let left = i + 1;
        let right = nums.length - 1;
        while (left < right) {
          const sum = nums[i] + nums[left] + nums[right];
          if (sum > 0) {
            right--;
          } else if (sum < 0) {
            left++;
          } else {
            result.push([nums[i], nums[left++], nums[right--]]);
            // 跳过重复数字
            while (nums[left] === nums[left - 1]) {
              left++;
            }
            // 跳过重复数字
            while (nums[right] === nums[right + 1]) {
              right--;
            }
          }
        }
      }
      return result;
    }

python

class Solution:
  	def threeSum(self, nums: List[int]) -> List[List[int]]:
      	nums.sort()
        ans = []
        seen = set()
        n = len(nums)
        for i in range(n):
          	a = nums[i]
            if a in seen: 
              	continue
            seen.add()
            d = {}
            for j in range(i+1,n):
              	b = nums[j]
                if b in d and (not(ans) or (ans[-1][0]!= a or ans[-1][2] != b)):
                  	ans.append((a,-a-b,b))
                else:
                  	d[-a-b] = 0
        return ans

最接近的三数之和16

给定一个包括 n 个整数的数组 nums 和 一个目标值 target。找出 nums 中的三个整数,使得它们的和与 target 最接近。返回这三个数的和。假定每组输入只存在唯一答案。

class Solution:
    def threeSumClosest(self, nums: List[int], target: int) -> int:
        nums.sort()
        n = len(nums)
        nearest = 10 ** 7

        for i in range(n - 2):
            lo, hi = i + 1, n - 1
            # 双指针
            while lo < hi:
                total = nums[i] + nums[lo] + nums[hi]
                if total == target:
                    return total
                elif total < target:
                    lo += 1
                else:
                    hi -= 1
                # 直接根据"最近"的定义取结果
                nearest = min(nearest, total, key=lambda x: abs(x - target))
        
        return nearest

N数之和

// 参数依次为目标数组、选取元素数目、目标和
const search = (arr, count, sum) => {
  // 计算某选择情况下有几个 1,也就是选择元素的个数
  const getCount = num => {
    let count = 0
    while(num) {
      num &= (num - 1)
      count++
    }
    return count
  }

  let len = arr.length, bit = 1 << len, res = []
  
  // 遍历所有的选择情况
  for(let i = 1; i < bit; i++){
    // 满足选择的元素个数 === count
    if(getCount(i) === count){
      let s = 0, temp = []

      // 每一种满足个数为 N 的选择情况下,继续判断是否满足 和为 M
      for(let j = 0; j < len; j++){
        // 建立映射,找出选择位上的元素
        if(i & 1 << (len - 1 -j)) {
          s += arr[j]
          temp.push(arr[j])
        }
      }

      // 如果这种选择情况满足和为 M
      if(s === sum) {
        res.push(temp)
      }
    }
  }

  return res
}

两个数组的交集349

给定两个数组,编写算法计算他们的交集

例:输入:nums1 = [1,2,2,1], nums2 = [2,2] 输出:[2,2]

js

var intersection = function(nums1, nums2) {
    // 根据数组大小交换操作的数组
    if(nums1.length < nums2.length) {
        const _ = nums1;
        nums1 = nums2;
        nums2 = _;
    }
    const nums1Set = new Set(nums1);
    const resSet = new Set();
    // for(const n of nums2) {
    //     nums1Set.has(n) && resSet.add(n);
    // }
    // 循环 比 迭代器快
    for(let i = nums2.length - 1; i >= 0; i--) {
        nums1Set.has(nums2[i]) && resSet.add(nums2[i]);
    }
    return Array.from(resSet);
};

Python

class Solution(object):
    def intersect(self, nums1, nums2):
        """
        :type nums1: List[int]
        :type nums2: List[int]
        :rtype: List[int]
        """
        inter = set(nums1) & set(nums2)
        l = []
        for i in inter:
            l += [i] * min(nums1.count(i), nums2.count(i))  
        return l

两个数组的交集II 350

交集中可重复

var intersect = function (nums1, nums2) {
// hash法
  const [hash,res] = [new Map(),[]]
  nums1.forEach(el=>{
    if (hash.has(el)) {
      hash.set(el, hash.get(el) + 1)
    } else {
      hash.set(el, 1)
    }
  })
  nums2.forEach(el=>{
    const hashKey = hash.get(el)
    if (hash.has(el)) {
      res.push(el)
      if (hashKey > 1) {
        hash.set(el, hashKey - 1)
      } else {
        hash.delete(el)
      }
    }
  })
  return res
}

双指针法

let p1 = 0
let p2 = 0
let res = []
nums1 = nums1.sort((a, b) => a - b)
nums2 = nums2.sort((a, b) => a - b)
while (p1 < nums1.length && p2 < nums2.length) {
    if (nums1[p1] == nums2[p2]) {
       res.push(nums1[p1])
       p1++
       p2++
     } else if (nums1[p1] < nums2[p2]) {
       p1++
     } else {
       p2++
     }
 }
 return res

四数之和18

给你一个由 n 个整数组成的数组 nums ,和一个目标值 target 。请你找出并返回满足下述全部条件且不重复的四元组 [nums[a], nums[b], nums[c], nums[d]] :

0 <= a, b, c, d < n a、b、c 和 d 互不相同 nums[a] + nums[b] + nums[c] + nums[d] == target 你可以按 任意顺序 返回答案 。

例:输入:nums = [1,0,-1,0,-2,2], target = 0 输出:[[-2,-1,1,2],[-2,0,0,2],[-1,0,0,1]]

class Solution:
    def fourSum(self, nums: List[int], target: int) -> List[List[int]]:
      	nums.sort()
        n = len(nums)
        res = []
        for i in range(n):
            if i > 0 and nums[i] == nums[i - 1]: continue
            for k in range(i+1, n):
                if k > i + 1 and nums[k] == nums[k-1]: continue
                p = k + 1
                q = n - 1

                while p < q:
                    if nums[i] + nums[k] + nums[p] + nums[q] > target: q -= 1
                    elif nums[i] + nums[k] + nums[p] + nums[q] < target: p += 1
                    else:
                        res.append([nums[i], nums[k], nums[p], nums[q]])
                        while p < q and nums[p] == nums[p + 1]: p += 1
                        while p < q and nums[q] == nums[q - 1]: q -= 1
                        p += 1
                        q -= 1
        return res

缺失的第一个数(41)

给你一个未排序的整数数组 nums ,请你找出其中没有出现的最小的正整数

示例 2:

输入:nums = [3,4,-1,1]
输出:2
解释:1 在数组中,但 2 没有。

示例 3:

输入:nums = [7,8,9,11,12]
输出:1
解释:最小的正数 1 没有出现。

js

function firstMissingPositive(ary) {
  // 首选过滤到非整数
  ary = ary.filter(a => a > 0)
  // 判断正数数组是否为空
  if(ary.length) {
    // 先排序,从小到大排序
    ary.sort((a, b) => a - b)
    // 如果第一个元素不是 1 直接返回 1
    if (ary[0] !== 1) return 1
    
    // 遍历,只要下一个元素和当前当前元素的差值 > 1, 说明输出元素就是当前元素值 +1
    for (let i = 0; i < ary.length; i++) {
      if (ary[i + 1] - ary[i] > 1) {
        return ary[i] + 1
      }
    }
    // 如果数组是连续的正正数 如:[1,2,3]
    return ary.pop() +  1
  }
  return 1
}

最大数(179)

给定一组非负整数 nums,重新排列每个数的顺序(每个数不可拆分)使之组成一个最大的整数

注意:输出结果可能非常大,所以你需要返回一个字符串而不是整数

示例 1:

输入:nums = [10,2]
输出:"210"

示例 2:

输入:nums = [3,30,34,5,9]
输出:"9534330"

js

function getMaxNumber(arr) {
  return arr.reduce((acc = '', cur) => 
     Math.max(+`${acc}${cur}`, +`${cur}${acc}`)
	);
}

无序整数数组,找出第k大的值215

给定整数数组 nums 和整数 k,请返回数组中第 **k** 个最大的元素。

请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素。、

例:输入: [3,2,1,5,6,4] 和 k = 2 输出: 5

class Solution:
    def findKthLargest(self, nums: List[int], k: int) -> int:
      	if len(nums) == 1: return nums[0]
        mid = int(len(nums) // 2)
        left, right, mids = [], [], []
        for i in range(len(nums)):
            if nums[i] > nums[mid]:
                left.append(nums[i])
            elif nums[i] < nums[mid]:
                right.append(nums[i])
            else:
                mids.append(nums[i])
        if len(left) >= k:
            return  self.findKthLargest(left, k)
        elif len(mids) >= k - len(left):
            return nums[mid]
        else:
            return self.findKthLargest(right, k - len(left) - len(mids))

种花问题(605)

假设有一个很长的花坛,一部分地块种植了花,另一部分却没有。可是,花不能种植在相邻的地块上,它们会争夺水源,两者都会死去。

给你一个整数数组 flowerbed 表示花坛,由若干 01 组成,其中 0 表示没种植花,1 表示种植了花。另有一个数 n ,能否在不打破种植规则的情况下种入 n 朵花?能则返回 true ,不能则返回 false

示例 1:

输入:flowerbed = [1,0,0,0,1], n = 1
输出:true

示例 2:

输入:flowerbed = [1,0,0,0,1], n = 2
输出:false

js

var canPlaceFlowers = function (flowerbed, n) {
    flowerbed.unshift(0);
    flowerbed.push(0);
    for (let i = 1; i < flowerbed.length - 1; i++) {
        if (flowerbed[i - 1] === 0 && flowerbed[i] === 0 && flowerbed[i + 1] === 0) {
            flowerbed[i] = 1; // 种花!
            n--;
        }
    }
    return n <= 0;
};

三个无重叠子数组最大和(689)

给你一个整数数组 nums 和一个整数 k ,找出三个长度为 k 、互不重叠、且全部数字和最大的子数组,并返回这三个子数组。

以下标的数组形式返回结果,数组中的每一项分别指示每个子数组的起始位置(下标从 0 开始)。如果有多个结果,返回字典序最小的一个。

示例 1:

输入:nums = [1,2,1,2,6,7,5,1], k = 2
输出:[0,3,5]
解释:子数组 [1, 2], [2, 6], [7, 5] 对应的起始下标为 [0, 3, 5]。
也可以取 [2, 1], 但是结果 [1, 3, 5] 在字典序上更小。

示例 2:

输入:nums = [1,2,1,2,1,2,1,2,1], k = 2
输出:[0,2,4]

js

// 单个子数组的最大和
var maxSumOfOneSubarray = function(nums, k) {
    let ans = [];
    let sum1 = 0, maxSum1 = 0;
    for (let i = 0; i < nums.length; ++i) {
        sum1 += nums[i];
        if (i >= k - 1) {
            if (sum1 > maxSum1) {
                maxSum1 = sum1;
                ans = [i - k + 1];
            }
            sum1 -= nums[i - k + 1];
        }
    }
    return ans;
};

// 两个子数组的最大和
const maxSumOfTwoSubarrays = (nums, k) => {
    const ans = [0, 0];
    let sum1 = 0, maxSum1 = 0, maxSum1Idx = 0;
    let sum2 = 0, maxSum12 = 0;
    for (let i = k; i < nums.length; ++i) {
        sum1 += nums[i - k];
        sum2 += nums[i];
        if (i >= k * 2 - 1) {
            if (sum1 > maxSum1) {
                maxSum1 = sum1;
                maxSum1Idx = i - k * 2 + 1;
            }
            if (maxSum1 + sum2 > maxSum12) {
                maxSum12 = maxSum1 + sum2;
                ans[0] = maxSum1Idx;
                ans[1] = i - k + 1;
            }
            sum1 -= nums[i - k * 2 + 1];
            sum2 -= nums[i - k + 1];
        }
    }
    return ans;
}

// 三个子数组的最大值
const maxSumOfThreeSubarrays = (nums, k) => {
    const ans = [0, 0, 0];
    let sum1 = 0, maxSum1 = 0, maxSum1Idx = 0;
    let sum2 = 0, maxSum12 = 0, maxSum12Idx1 = 0, maxSum12Idx2 = 0;
    let sum3 = 0, maxTotal = 0;
    for (let i = k * 2; i < nums.length; ++i) {
        sum1 += nums[i - k * 2];
        sum2 += nums[i - k];
        sum3 += nums[i];
        if (i >= k * 3 - 1) {
            if (sum1 > maxSum1) {
                maxSum1 = sum1;
                maxSum1Idx = i - k * 3 + 1;
            }
            if (maxSum1 + sum2 > maxSum12) {
                maxSum12 = maxSum1 + sum2;
                maxSum12Idx1 = maxSum1Idx;
                maxSum12Idx2 = i - k * 2 + 1;
            }
            if (maxSum12 + sum3 > maxTotal) {
                maxTotal = maxSum12 + sum3;
                ans[0] = maxSum12Idx1;
                ans[1] = maxSum12Idx2;
                ans[2] = i - k + 1;
            }
            sum1 -= nums[i - k * 3 + 1];
            sum2 -= nums[i - k * 2 + 1];
            sum3 -= nums[i - k + 1];
        }
    }
    return ans;
}

pairsArray

找出数组中所有和为target的二元数组

例:nums = [1,2,2,2,5,-1,3, 1], target = 4

输出:[1,3], [3,1], [2,2], [2,2], [5,-1]

暴力循环O(n平方)

获取所有的二元子数组,判断求和

双指针法O(n)

const pairsArray = (nums, sum) => {
  const result = [];
  const sortedNums = nums.sort((a, b) => a - b);
  for (let i = 0; i < sortedNums.length; i++) {
    let left = i;
    let right = sortedNums.length - 1;
    while (left < right) {
      if (sortedNums[left] + sortedNums[right] > sum) {
        right -= 1;
      } else if (sortedNums[left] + sortedNums[right] < sum) {
        left += 1;
      } else {
        result.push([sortedNums[left], sortedNums[right]]);
        break;
      }
    }
  }
  return result;
};

买卖股票的最佳时机1、2、3、4(121,122,123,188,309)

给定数组prices,第i个元素表示股票第i天的价格

需选择在某一天买入,并在某一天卖出,设计一个算法计算获取的最大利润

例:输入:[7,1,5,3,6,4]

输出:5 在第二天买入,第5天卖出,6-1=5

class Solution:
    def maxProfit(self,prices:List[int])-> int:
        min_p,max_v = float('inf'),0
        for p in prices:
            min_p = min(min_p,p)
            max_v = max(max_v,p-min_p)
        return max_v

122给定一个数组 prices ,其中 prices[i] 是一支给定股票第 i 天的价格。

设计一个算法来计算你所能获取的最大利润。你可以尽可能地完成更多的交易(多次买卖一支股票)。

注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。

输入: prices = [7,1,5,3,6,4] 输出: 7 解释: 在第 2 天(股票价格 = 1)的时候买入,在第 3 天(股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 。 随后,在第 4 天(股票价格 = 3)的时候买入,在第 5 天(股票价格 = 6)的时候卖出, 这笔交易所能获得利润 = 6-3 = 3 。

贪心

class Solution:
    def maxProfit(self, prices: List[int]) -> int:
        ans = 0
        for i in range(1, len(prices)):
            if prices[i] > prices[i-1]:
                ans += prices[i] - prices[i-1]
        return ans

dp

class Solution:
    def maxProfit(self, prices: List[int]) -> int:
        if not prices:
            return 0
        n = len(prices)
        dp = [[0]*2 for _ in range(n)]
        # dp[i][0]表示第i天不持有股票, dp[i][1]表示第i天持有股票
        dp[0][0], dp[0][1] = 0, - prices[0]
        for i in range(1, n):
            dp[i][0] = max(dp[i-1][0], dp[i-1][1] + prices[i])
            dp[i][1] = max(dp[i-1][1], dp[i-1][0] - prices[i])
        return dp[n-1][0]

123:给定一个数组,它的第 i 个元素是一支给定的股票在第 i 天的价格。

设计一个算法来计算你所能获取的最大利润。你最多可以完成 两笔 交易。

注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。

输入:prices = [3,3,5,0,0,3,1,4] 输出:6 解释:在第 4 天(股票价格 = 0)的时候买入,在第 6 天(股票价格 = 3)的时候卖出,这笔交易所能获得利润 = 3-0 = 3 。 随后,在第 7 天(股票价格 = 1)的时候买入,在第 8 天 (股票价格 = 4)的时候卖出,这笔交易所能获得利润 = 4-1 = 3 。

class Solution:
    def maxProfit(self, prices: List[int]) -> int:
        if len(prices) == 0:
            return 0
        dp = [0] * 5 
        dp[1] = -prices[0]
        dp[3] = -prices[0]
        for i in range(1, len(prices)):
            dp[1] = max(dp[1], dp[0] - prices[i])
            dp[2] = max(dp[2], dp[1] + prices[i])
            dp[3] = max(dp[3], dp[2] - prices[i])
            dp[4] = max(dp[4], dp[3] + prices[i])
        return dp[4]

给定一个整数数组 prices ,它的第 i 个元素 prices[i] 是一支给定的股票在第 i 天的价格。

设计一个算法来计算你所能获取的最大利润。你最多可以完成 k 笔交易。

注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。

输入:k = 2, prices = [2,4,1] 输出:2 解释:在第 1 天 (股票价格 = 2) 的时候买入,在第 2 天 (股票价格 = 4) 的时候卖出,这笔交易所能获得利润 = 4-2 = 2 。

class Solution:
    def maxProfit(self, k: int, prices: List[int]) -> int:
        if len(prices) == 0:
            return 0
        dp = [[0] * (2*k+1) for _ in range(len(prices))]
        for j in range(1, 2*k, 2):
            dp[0][j] = -prices[0]
        for i in range(1, len(prices)):
            for j in range(0, 2*k-1, 2):
                dp[i][j+1] = max(dp[i-1][j+1], dp[i-1][j] - prices[i])
                dp[i][j+2] = max(dp[i-1][j+2], dp[i-1][j+1] + prices[i])
        return dp[-1][2*k]

309:给定一个整数数组,其中第 i 个元素代表了第 i 天的股票价格 。

设计一个算法计算出最大利润。在满足以下约束条件下,你可以尽可能地完成更多的交易(多次买卖一支股票):

你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。 卖出股票后,你无法在第二天买入股票 (即冷冻期为 1 天)。

输入: [1,2,3,0,2] 输出: 3 解释: 对应的交易状态为: [买入, 卖出, 冷冻期, 买入, 卖出]

class Solution:
    def maxProfit(self, prices: List[int]) -> int:
        n = len(prices)
        if n == 0:
            return 0
        dp = [[0] * 4 for _ in range(n)]
        dp[0][0] = -prices[0] #持股票
        for i in range(1, n):
            dp[i][0] = max(dp[i-1][0], max(dp[i-1][3], dp[i-1][1]) - prices[i])
            dp[i][1] = max(dp[i-1][1], dp[i-1][3])
            dp[i][2] = dp[i-1][0] + prices[i]
            dp[i][3] = dp[i-1][2]
        return max(dp[n-1][3], dp[n-1][1], dp[n-1][2])

连续子数组最大和(53)

给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。

例:输入:nums = [-2,1,-3,4,-1,2,1-5,4]

输出:6,连续子数组[4,-1,2,1]的和最大,为6

class Solution:
    def maxSubArray(self, nums: List[int]) -> int:
        for i in range(1, len(nums)):
            nums[i]= nums[i] + max(nums[i-1], 0)
        return max(nums)

乘积最大子数组(152)

给你一个整数数组 nums ,请你找出数组中乘积最大的连续子数组(该子数组中至少包含一个数字),并返回该子数组所对应的乘积。

例:输入:[2,3,-2,4]

输出:6

class Solution:
    def maxProduct(self, nums: List[int]) -> int:
        B = nums[::-1]
        for i in range(1, len(nums)):
            nums[i] *= nums[i - 1] or 1
            B[i] *= B[i - 1] or 1
        return max(max(nums),max(B))

组合、组合总和1、2、3(77、39、40、216)

组合

给定两个整数 nk,返回范围 [1, n] 中所有可能的 k 个数的组合。

你可以按 任何顺序 返回答案。

例:输入:n = 4, k = 2 输出: [ [2,4], [3,4], [2,3], [1,2], [1,3], [1,4], ]

class Solution:
    def combine(self, n: int, k: int) -> List[List[int]]:
        res = []
        path = []
        def backtrack(n, k, StartIndex):
            if len(path) == k:
                res.append(path[:])
                return
            for i in range(StartIndex, n-(k-len(path)) + 2):
                path.append(i)
                backtrack(n, k, i+1)
                path.pop()
        backtrack(n, k, 1)
        return res

给定一个无重复元素的正整数数组 candidates 和一个正整数 target ,找出 candidates 中所有可以使数字和为目标数 target 的唯一组合。

candidates 中的数字可以无限制重复被选取。如果至少一个所选数字数量不同,则两种组合是唯一的。

对于给定的输入,保证和为 target 的唯一组合数少于 150 个。

例:输入: candidates = [2,3,6,7], target = 7 输出: [[7],[2,2,3]]

class Solution:
    def combinationSum(self, candidates: List[int], target: int) -> List[List[int]]:
      	candidates.sort()
        n = len(candidates)
        res = []
        def helper(i, tmp_sum, tmp):
            if tmp_sum > target or i == n:
                return 
            if tmp_sum == target:
                res.append(tmp)
                return 
            helper(i,  tmp_sum + candidates[i],tmp + [candidates[i]])
            helper(i+1, tmp_sum ,tmp)
        helper(0, 0, [])
        return res

40:

给定一个数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。

candidates 中的每个数字在每个组合中只能使用一次。

注意:解集不能包含重复的组合。

例:输入: candidates = [2,5,2,1,2], target = 5, 输出: [ [1,2,2], [5] ]

class Solution:
    def combinationSum2(self, candidates: List[int], target: int) -> List[List[int]]:
      	if not candidates:
            return []
        candidates.sort()
        n = len(candidates)
        res = []
        
        def backtrack(i, tmp_sum, tmp_list):
            if tmp_sum == target:
                res.append(tmp_list)
                return 
            for j in range(i, n):
                if tmp_sum + candidates[j]  > target : break
                if j > i and candidates[j] == candidates[j-1]:continue
                backtrack(j + 1, tmp_sum + candidates[j], tmp_list + [candidates[j]])
        backtrack(0, 0, [])    
        return res

组合总和3

找出所有相加之和为 n 的 k 个数的组合。组合中只允许含有 1 - 9 的正整数,并且每种组合中不存在重复的数字。

说明:

所有数字都是正整数。 解集不能包含重复的组合。

输入: k = 3, n = 9 输出: [[1,2,6], [1,3,5], [2,3,4]]

class Solution:
    def combinationSum3(self, k: int, n: int) -> List[List[int]]:
        res = []  #存放结果集
        path = []  #符合条件的结果
        def findallPath(n,k,sum,startIndex):
            if sum > n: return  #剪枝操作
            if sum == n and len(path) == k:  #如果path.size() == k 但sum != n 直接返回
                return res.append(path[:])
            for i in range(startIndex,9-(k-len(path))+2):  #剪枝操作
                path.append(i)
                sum += i 
                findallPath(n,k,sum,i+1)  #注意i+1调整startIndex
                sum -= i  #回溯
                path.pop()  #回溯
        
        findallPath(n,k,0,1)
        return res

全排列(46、47)

46:给一个不含重复数组,返回其所有可能的全排列

输入:[1,2,3]

输出:[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]

js

let func = (arr) => {
  let len = arr.length
  let res = [] // 所有排列结果
  
  let arrange = (tempArr, leftArr) => {
    if (tempArr.length === len) { // 这里就是递归结束的地方
      // res.push(tempArr) // 得到全排列的每个元素都是数组
      res.push(tempArr.join('')) // 得到全排列的每个元素都是字符串
    } else {
      leftArr.forEach((item, index) => {
        let temp = [].concat(leftArr)
        temp.splice(index, 1)
        // 此时,第一个参数是当前分离出的元素所在数组;第二个参数temp是传入的leftArr去掉第一个后的结果
        arrange(tempArr.concat(item), temp) // 这里使用了递归
      })
    }
  }
  arrange([], arr)
  return res
}
console.log('结果:', func(['A', 'B', 'C', 'D']))
class Solution:
   def permute(self,nums:List[int])->List[List[int]]:
       res = []
       def backtrack(nums,tmp):
           if not nums:
               res.append(tmp)
               return 
           for i in range(len(nums)):
               backtrack(nums[:i]+nums[i+1],tmp+[nums[i]])
       backtrack(nums,[])
       return res

47:给定一个可包含重复数字的序列 nums按任意顺序 返回所有不重复的全排列。

输入:nums = [1,1,2];

输出: [[1,1,2], [1,2,1], [2,1,1]]

class Solution:
    def permuteUnique(self, nums: List[int]) -> List[List[int]]:
      	# res用来存放结果
        if not nums: return []
        res = []
        used = [0] * len(nums)
        def backtracking(nums, used, path):
            # 终止条件
            if len(path) == len(nums):
                res.append(path.copy())
                return
            for i in range(len(nums)):
                if not used[i]:
                    if i>0 and nums[i] == nums[i-1] and not used[i-1]:
                        continue
                    used[i] = 1
                    path.append(nums[i])
                    backtracking(nums, used, path)
                    path.pop()
                    used[i] = 0
        # 记得给nums排序
        backtracking(sorted(nums),used,[])
        return res

子集78、90

给你一个整数数组 nums ,数组中的元素 互不相同 。返回该数组所有可能的子集(幂集)。

解集 不能 包含重复的子集。你可以按 任意顺序 返回解集。

输入:nums = [1,2,3] 输出:[[],[1],[2],[1,2],[3],[1,3],[2,3],[1,2,3]]

class Solution:
    def subsets(self, nums: List[int]) -> List[List[int]]:
      	res = []  
        path = []  
        def backtrack(nums,startIndex):
            res.append(path[:])  #收集子集,要放在终止添加的上面,否则会漏掉自己
            for i in range(startIndex,len(nums)):  #当startIndex已经大于数组的长度了,就终止了,for循环本来也结束了,所以不需要终止条件
                path.append(nums[i])
                backtrack(nums,i+1)  #递归
                path.pop()  #回溯
        backtrack(nums,0)
        return res

90:给你一个整数数组 nums ,其中可能包含重复元素,请你返回该数组所有可能的子集(幂集)。

解集 不能 包含重复的子集。返回的解集中,子集可以按 任意顺序 排列。

输入:nums = [1,2,2] 输出:[[],[1],[1,2],[1,2,2],[2],[2,2]]

class Solution:
    def subsetsWithDup(self, nums: List[int]) -> List[List[int]]:
       	res = []  #存放符合条件结果的集合
        path = []  #用来存放符合条件结果
        def backtrack(nums,startIndex):
            res.append(path[:])
            for i in range(startIndex,len(nums)):
                if i > startIndex and nums[i] == nums[i - 1]:  #我们要对同一树层使用过的元素进行跳过
                    continue
                path.append(nums[i])
                backtrack(nums,i+1)  #递归
                path.pop()  #回溯
        nums = sorted(nums)  #去重需要排序
        backtrack(nums,0)
        return res

移除元素(27)

给出一个数组和一个值val,你需要原地移除所有数值等于val的元素,并返回移除后数组的长度

例:输入:nums = [3,2,2,3] val= 3

输出:2,nums = [2,2]

快慢指针

class Solution:
  def removeElement(self, nums: List[int], val: int) -> int:
    	i = 0
      for j in range(len(nums)):
        	if nums[j] != val:
            	nums[i] = nums[j]
              i += 1
      return i

下一个排列(31)

实现获取 下一个排列 的函数,算法需要将给定数字序列重新排列成字典序中下一个更大的排列。

如果不存在下一个更大的排列,则将数字重新排列成最小的排列(即升序排列)。

例:输入 nums = [1,2,3]。这个数是123,找出1,2,3这3个数字排序可能的所有数,排序后,比123大的那个数 也就是132

输入 nums = [3,2,1]。这就是1,2,3所有排序中最大的那个数,那么就返回1,2,3排序后所有数中最小的那个,也就是1,2,3 -> [1,2,3]

class Solution:
    def nextPermutation(self, nums: List[int]) -> None:
				i = len(nums) - 2
        while i >= 0 and nums[i] >= nums[i+1]:
            i -= 1

        # i<0时已经为最大的排列
        if i >= 0:
            j = len(nums) - 1
            while j >= 0 and nums[j] <= nums[i]:
                j -= 1

            nums[i], nums[j] = nums[j], nums[i]

        l = nums[i+1:]
        l.reverse()
        nums[i+1:] = l 

旋转数组(189)

给定一个数组,将数组中的元素向右移动k个位置,其中k是非负数

输入:nums = [1,2,3,4,5,6,7], k = 3

输出:[5,6,7,1,2,3,4]

class Solution:
  	def rotate(self, nums:List[int], k:int)-> None:
      	nums[: ] = (nums[i] for i in range(-(k % len(nums)),len(nums)-k % len(nums)))

最长连续递增子序列(300)

给你一个整数数组 nums ,找到其中最长严格递增子序列的长度。

子序列是由数组派生而来的序列,删除(或不删除)数组中的元素而不改变其余元素的顺序。例如,[3,6,2,7] 是数组 [0,3,1,6,2,2,7] 的子序列。

例如:输入:[10,9,2,5,3,7,101,18]

输出:4 ,最长递增子序列为[2,5,7,101]

输入:[0,1,0,3,2,3]

输出:4,最长递增子序列为[0,1,2,3]

class Solution:
    def lengthOfLIS(self, nums: List[int]) -> int:
        n = len(nums)
        dp = [1 for _ in range(n)]
        for i in range(1, n):
            for j in range(i):
                if nums[j] < nums[i]:
                    dp[i] = max(dp[i], dp[j] + 1)
        return max(dp)

连续子数组的最大和(剑指offer42)

输入一个整型数组,数组中的一个或连续多个整数组成一个子数组。求所有子数组的和的最大值。

要求时间复杂度为O(n)。

输入: nums = [-2,1,-3,4,-1,2,1,-5,4] 输出: 6 解释: 连续子数组 [4,-1,2,1] 的和最大,为 6。

class Solution:
    def maxSubArray(self, nums: List[int]) -> int:
        dp = [0] * len(nums)
        dp[0], max_sum = nums[0], nums[0]
        for i in range(1, len(nums)):
            dp[i] = max(dp[i-1] + nums[i], nums[i])
            max_sum = max(max_sum, dp[i])
        return max_sum

数组拼接最小数

只出现过一次的数字(136)

给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。

例:输入:[2,2,1]

输出:1

class Solution:
    def singleNumber(self, nums: List[int]) -> int:
        a = 0
        for num in nums:
            a = a ^ num
        return a

只出现过一次的数字2(137)

给你一个整数数组 nums ,除某个元素仅出现 一次 外,其余每个元素都恰出现 三次 。请你找出并返回那个只出现了一次的元素。

例:输入:[2,2,3,2]

输出:3

class Solution:
    def singleNumber(self, nums: List[int]) -> int:
        nums.sort()
        for i in range(0,len(nums)-3,3):
            if nums[i]!=nums[i+1]:
                return nums[i]
        return nums[-1]

缺失的第一个正数(41)

给你一个未排序的整数数组nums,请你找出其中没有出现的最小的正整数

例:输入:[1,2,0]

输出:4

class Solutioon:
  	def firstMissingPositive(self,nums:List[int])-> int:
      	nums = set(nums)
        for j in range(1, 2 ** 31 - 1):
          	if j not in nums:
              	return j

颜色分类(75)

给定一个包含红色、白色和蓝色,一共 n 个元素的数组,原地对它们进行排序,使得相同颜色的元素相邻,并按照红色、白色、蓝色顺序排列。

此题中,我们使用整数 0、 1 和 2 分别表示红色、白色和蓝色。

例:输入:nums = [2,0,2,1,1,0]

输出:[0,0,1,1,2,2]

快排

class Solution:
    def sortColors(self, nums: List[int]) -> None:
        

柱状图中最大的矩形(84)

给定 n 个非负整数,用来表示柱状图中各个柱子的高度。每个柱子彼此相邻,且宽度为 1 。

求在该柱状图中,能够勾勒出来的矩形的最大面积。

输入: [2,1,5,6,2,3] 输出: 10

class Solution:
    def largestRectangleArea(self, heights: List[int]) -> int:
        ans, s, hs = 0, [0], [0, *heights, 0]
        for i, h in enumerate(hs):
            while hs[s[-1]] > h:
                ans = max(ans, (i - s[-2] - 1) * hs[s.pop()])
            s.append(i)
        return ans

长度最小的子数组(209)

给定一个含有n个正整数和一个正整数target

找出该数组中满足其和大于target的长度最小的连续子数组,并返回其长度

例:nums=[2,3,1,2,4,3], target=7

输出:2

class Solution:
  	def minSubArrayLen(self, target:int,nums: List[int])-> int:
      	i, ans = 0, len(A) + 1
        for j in range(len(A)):
          	s -= A[j]
            while s <= 0:
              	ans = min(ans,j-i+1)
                s += A[i]
                i += 1
        return ans % (len(A)+1)

存在重复元素(217)

给定一个整数数组,判断是否存在重复元素。

如果存在一值在数组中出现至少两次,函数返回 true 。如果数组中每个元素都不相同,则返回 false

例:输入:[1,2,3,1]

输出:true

输入:[1,2,3,4]

输出:false

class Solution:
    def containsDuplicate(self, nums: List[int]) -> bool:
      	return not (len(nums)==len(set(nums)))

存在重复元素2(219)

给定一个整数数组和一个整数 k,判断数组中是否存在两个不同的索引 i 和 j,使得 nums [i] = nums [j],并且 i 和 j 的差的 绝对值 至多为 k。

例:输入:nums = [1,2,3,1], k = 3

输出:true

输入:nums = [1,2,3,1,2,3], k = 2

输出:false

class Solution:
    def containsNearbyDuplicate(self, nums: List[int], k: int) -> bool:
      	nums_len = len(nums)
        if nums_len <= 1:
            return False
        nums_dict = {}
        for i in range(nums_len):
            if nums[i] in nums_dict:
                if i-nums_dict[nums[i]] <= k:
                    return True
            nums_dict[nums[i]] = i
        return False

存在重复元素3(220)

给你一个整数数组 nums 和两个整数 k 和 t 。请你判断是否存在 两个不同下标 i 和 j,使得 abs(nums[i] - nums[j]) <= t ,同时又满足 abs(i - j) <= k 。

如果存在则返回 true,不存在返回 false。

例:输入:nums = [1,2,3,1], k = 3, t = 0

输出:true

输入:nums = [1,5,9,1,5,9], k = 2, t = 3

输出:false

class Solution:
    def containsNearbyAlmostDuplicate(self, nums: List[int], k: int, t: int) -> bool:
      	ls = [[nums[i], i] for i in range(len(nums))]
        ls.sort(key=lambda x: x[0])
        for i in range(len(nums)):
            for j in range(i + 1, len(nums)):
                if ls[j][0] - ls[i][0] > t:
                    break
                if abs(ls[j][1] - ls[i][1]) <= k:
                    return True
        return False

寻找重复数字287

给定一个包含 n + 1 个整数的数组 nums ,其数字都在 1 到 n 之间(包括 1 和 n),可知至少存在一个重复的整数。

假设 nums 只有 一个重复的整数 ,找出 这个重复的数 。

你设计的解决方案必须不修改数组 nums 且只用常量级 O(1) 的额外空间。

例:输入:nums = [1,3,4,2,2] 输出:2

class Solution:
    def findDuplicate(self, nums: List[int]) -> int:
      	lo, hi = 1, len(nums)-1
        while lo < hi:
            mid = (lo + hi) // 2
            if sum(i<=mid for i in nums) <= mid:
                lo = mid + 1
            else:
                hi = mid
        return lo

数组中重复的数据(442)

给定一个整数数组a,其中有些元素出现了两次而有些元素出现了一次,找出所有出现两次的元素

输入:[4,3,2,7,8,2,3,1]

输出:[2,3]

class Solution:
  	def findDuplicates(self, nums:List[int])-> List[int]:
      	res = []
        for x in nums:
          	if nums[abs(x-1)] < 0:
              	res.append(abs(x))
            else:
              	nums[abs(x)-1] *= -1
        return res

寻找峰值162

峰值元素是指其值严格大于左右相邻值的元素。

给你一个整数数组 nums,找到峰值元素并返回其索引。数组可能包含多个峰值,在这种情况下,返回 任何一个峰值 所在位置即可。

你可以假设 nums[-1] = nums[n] = -∞ 。

例:输入:nums = [1,2,1,3,5,6,4] 输出:1 或 5

class Solution:
    def findPeakElement(self, nums: List[int]) -> int:
      	lo, hi = 0, len(nums)-1
        while lo < hi:
            mid = (lo+hi) // 2
            mid2 = mid + 1
            if nums[mid] < nums[mid2]:
                lo = mid2
            else:
                hi = mid
        return lo

多数元素(169)

波义尔摩尔投票算法

def majorityElement(self, nums):
  	count = 0
    candidate = None
    for num in nums:
      	if count == 0:
          	candidate = num
        count += (1 if num == candidate else -1)
    return candidate

求众数(229)

给定一个大小为n的整数组,找出其中所有出现超过n/3次的元素

输入:[3,2,3]

输出:[3]

class Solution:
  	def majorityElement(self, nums:List[int]) -> Lisr[int]:
      	count1, count2, candidate1, candidate2 = 0,0,0,1
        for n in nums:
          	if n == candidate1:
              	count1 += 1
            elif n == candidate2:
              	count2 += 1
            elif count1 == 0:
              	candidate1, count1 = n,1
            elif count2 == 0:
              	candidate2, count2 = n,1
            else:
              	count1, count2 = count1 - 1,count2 - 1
        return [n for n in (candidate1,candidate2)
                if nums.count(n) > len(nums) // 3]

卡牌分组(914)

给定一副牌,每张牌上都写着一个整数。

此时,你需要选定一个数字 X,使我们可以将整副牌按下述规则分成 1 组或更多组:

  • 每组都有 X 张牌。
  • 组内所有的牌上都写着相同的整数。

仅当你可选的 X >= 2 时返回 true

示例 1:

输入:deck = [1,2,3,4,4,3,2,1]
输出:true
解释:可行的分组是 [1,1],[2,2],[3,3],[4,4]

示例 2:

输入:deck = [1,1,1,2,2,2,3,3]
输出:false
解释:没有满足要求的分组。
var hasGroupsSizeX = function (deck) {
  if (!deck.length || deck.length === 1) return false;
  let obj = {};
  for (let i of deck) {
    obj[i] = !obj[i] ? 1 : ++obj[i];
  }
  const sizeArr = Object.values(obj);
  const minSize = Math.min.apply(null, sizeArr);
  for (let i = 2; i <= minSize; i++) {
    if (sizeArr.every(key => !(key % i))) return true;
  }
  return false;
};

有序数组

在排序数组中查找元素的第一个和最后一个位置34

给定一个按照升序排列的整数数组 nums,和一个目标值 target。找出给定目标值在数组中的开始位置和结束位置。

如果数组中不存在目标值 target,返回 [-1, -1]。

例:输入:nums = [5,,7,7,8,8,10],target = 8

输出:[3,4]

class Solution:
    def searchRange(self, nums: List[int], target: int) -> List[int]:
        res = [-1, -1]

        l, r = 0, len(nums) - 1
        while l < r:
            mid = l + (r - l) // 2  # 向左取整
            if nums[mid] < target:  # 首先考虑一定不存在解的区间,则有+1(或者-1)
                l = mid + 1
            else:
                r = mid
        if not nums or nums[l] != target:  # 用例中有nums = [] 的情况
            return res
        res[0] = l
        
        l, r = r, len(nums) - 1
        while l < r:
            mid = l + (r - l + 1) // 2  # 向右取整
            if nums[mid] > target:
                r = mid - 1
            else:
                l = mid
        res[1] = r
        return res

搜索插入位置35

给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。

你可以假设数组中无重复元素。

例:输入:[1,3,5,6],5

输出:2

输入:[1,3,5,6],2

输出:1

二分法查找

class Solution:
    def searchInsert(self, nums: List[int], target: int) -> int:
        low = 0
        high = len(nums)
        while low < high:
            mid = low + (high - low)//2
            if nums[mid] > target:
                high = mid
            elif nums[mid] < target:
                low = mid +1
            else:
                return mid
        return low

合并区间(56)

以数组 intervals 表示若干个区间的集合,其中单个区间为 intervals[i] = [starti, endi] 。请你合并所有重叠的区间,并返回一个不重叠的区间数组,该数组需恰好覆盖输入中的所有区间。

例:输入:[1,3],[2,6],[8,10],[15,18]

输出:[1,6],[8,10],[15,18]

先排序,然后判断是否合并区间

class Solution:
    def merge(self,intervals:List[List[int]])->List[List[int]]:
        if intervals == []:
           return []
        intervals.sort(key = lambda x:x[0])
        ##前一个合并区间
        pre = intervals[0]
        res = []
        for i in range(1,len(intervals)):
            if intervals[i][0] <= pre[1]:
               ##合并,更新pre结束位置
               if intervals[i][1] > pre[1]:
                  pre[1] = intervals[i][1]
            else
               res.append(pre)
               pre = intervals[i]
        ##最后一个没有加进去
        res.append(pre)
        return res

插入区间(57)

给你一个 无重叠的 按照区间起始端点排序的区间列表。

在列表中插入一个新的区间,你需要确保列表中的区间仍然有序且不重叠

例:输入:intervals = [1,3],[6,9],newInterval = [2,5]

输出:[[1,5],[6,9]]

class Solution:
     def insert(self, intervals: List[List[int]], newInterval: List[int]) -> List[List[int]]:
        intervals.append(newInterval)
        intervals = sorted(intervals, key=lambda x: x[0])

        merge = [intervals[0]]
        for i in range(1, len(intervals)):
            mergeleft = merge[-1][0]
            mergeright = merge[-1][1]
            interleft = intervals[i][0]
            interright = intervals[i][1]
            if interleft > mergeright:
                merge.append(intervals[i])
            else:
                merge[-1][1] = max(mergeright, interright)
        return merge

删除有序数组的重复项(26)

删除有序数组中重复的元素,在原数组上进行操作,

def removeDuplicates(nums):
  	if not nums:
      	return 0
    index = 1
    for i in range(len(nums)-1):
      	if nums[i] != nums[i+1]:
          	nums[index] = nums[i+1]
            index +=1
    return index

删除有序数组的重复项2(80)

给你一个有序数组 nums ,请你 原地 删除重复出现的元素,使每个元素 最多出现两次 ,返回删除后数组的新长度。

不要使用额外的数组空间,你必须在 原地 修改输入数组 并在使用 O(1) 额外空间的条件下完成。

例:输入:[1,1,1,2,2,3]

输出:[1,1,2,2,3]

class Solution:
    def removeDuplicates(self, nums: List[int]) -> int:
        i = 0
        for e in nums:
            if i < 2 or e != nums[i - 2]:
                nums[i] = e
                i += 1

        return i

合并排序数组(88)

给你两个有序整数数组 nums1 和 nums2,请你将 nums2 合并到 nums1 中,使 nums1 成为一个有序数组。

初始化 nums1 和 nums2 的元素数量分别为 m 和 n 。你可以假设 nums1 的空间大小等于 m + n,这样它就有足够的空间保存来自 nums2 的元素。

输入:nums1 = [1,2,3,0,0,0], m = 3, nums2 = [2,5,6], n = 3

输出:[1,2,2,3,5,6]

class Solution:
    def merge(self, nums1: List[int], m: int, nums2: List[int], n: int) -> None:
        i, j, k = m-1, n-1, m+n-1
        while i >=0 and j >= 0:
            if nums1[i] > nums2[j]:
                nums1[k] = nums1[i]
                i -= 1
            else:
                nums1[k] = nums2[j]
                j -= 1
            k -= 1
        while j >= 0:
            nums1[k] = nums2[j]
            j -= 1
            k -= 1

js

const merge = function(nums1, m, nums2, n) {
    let len1 = m - 1,
        len2 = n - 1,
        len = m + n - 1
    while(len2 >= 0) {
        if(len1 < 0) {
            nums1[len--] = nums2[len2--]
            continue
        }
        nums1[len--] = nums1[len1] >= nums2[len2] ? nums1[len1--]: nums2[len2--]
    }
};

function mergeAry(left = [], right = []) {
  const result = [];
  while (left.length && right.length) {
    result.push(left[0] <= right[0] ? left.shift() : right.shift());
  }
  return result.concat(left, right);
}

汇总区间(228)

给定一个无重复元素的有序整数数组,返回恰好覆盖数组中所有数组的最小有序区间范围列表,

例:nums = [0,1,2,4,5,7]

输出:["0->2","4->5","7"]

nums = [0,2,3,4,6,8,9]

输出:["0","2->4","6","8->9"]

class Solution:
  	def summaryRange(self, nums: List[int]) -> List[str]:
      	li = []
        i = 0
        while i < len(nums):
          	j = i + 1;
            while j < len(nums) and nums[j] == nums[i] + j - i:
              	j += 1
            li.append(str(nums[i]) if i == j-1 else str(nums[i]) + '->' + str(nums[j-1]))
            i = j
        return li

寻找右区间436

给你一个区间数组 intervals ,其中 intervals[i] = [starti, endi] ,且每个 starti 都 不同 。

区间 i 的 右侧区间 可以记作区间 j ,并满足 startj >= endi ,且 startj 最小化 。

返回一个由每个区间 i 的 右侧区间 的最小起始位置组成的数组。如果某个区间 i 不存在对应的 右侧区间 ,则下标 i 处的值设为 -1 。

例:输入:intervals = [[1,2]] 输出:[-1] 解释:集合中只有一个区间,所以输出-1。

输入:intervals = [[3,4],[2,3],[1,2]] 输出:[-1, 0, 1] 解释:对于 [3,4] ,没有满足条件的“右侧”区间。 对于 [2,3] ,区间[3,4]具有最小的“右”起点,在原数组中索引为0; 对于 [1,2] ,区间[2,3]具有最小的“右”起点,在原数组中索引为1。

class Solution:
    def findRightInterval(self, intervals: List[List[int]]) -> List[int]:
      	n = len(intervals)
        res = [-1] * n
        dic = {}
        for i, v in enumerate(intervals):
            dic[v[0]] = i
        start = sorted(dic.keys())
        for i, v in enumerate(intervals):
            l, r = 0, n - 1
            while l <= r:
                mid = (l + r) >> 1
                if v[1] > start[mid]:
                    l = mid + 1
                else:
                    r = mid - 1
            if l < n:
                res[i] = dic[start[l]]

        return res

和为s的连续正数序列(剑指offer57)

输入一个正整数 target ,输出所有和为 target 的连续正整数序列(至少含有两个数)。

序列内的数字由小到大排列,不同序列按照首个数字从小到大排列。

例:输入:target = 9

输出:[[2,3,4],[4,5]]

双指针滑动窗口

class Solution:
    def findContinuousSequence(self, target: int) -> List[List[int]]:
        n = target//2+1 
        nums = list(range(1,n+1)) 
        left,right = 0,0 
        sums,res = 0,[]
        while right<len(nums):
            sums+=nums[right] 
            right+=1 
            while sums>=target:
                if sums==target:
                    res.append(nums[left:right]) 
                sums-=nums[left] 
                left+=1 
        return res

多维数组

最大正方形(221)

在一个由 '0''1' 组成的二维矩阵内,找到只包含 '1' 的最大正方形,并返回其面积。

例:输入:matrix = [["1","0","1","0","0"],["1","0","1","1","1"],["1","1","1","1","1"],["1","0","0","1","0"]]

输出:4

class Solution:
    def maximalSquare(self, matrix: List[List[str]]) -> int:
        if not matrix or not matrix[0]:
            return 0
        m, n = len(matrix), len(matrix[0]) 
        dp = [[0] * (n + 1) for _ in range(m + 1)]
        for i in range(1, m + 1):
            for j in range(1, n + 1):
                if matrix[i - 1][j - 1] == '1':
                    dp[i][j] = min(dp[i-1][j-1], dp[i][j-1], dp[i-1][j]) + 1
        return max(map(max, dp)) ** 2

最大矩形(85)

给定一个仅包含 01 、大小为 rows x cols 的二维二进制矩阵,找出只包含 1 的最大矩形,并返回其面积。

例:输入:matrix = [["1","0","1","0","0"],["1","0","1","1","1"],["1","1","1","1","1"],["1","0","0","1","0"]]

输出:6

class Solution:
    def maximalRectangle(self, matrix: List[List[str]]) -> int:
        if len(matrix) == 0:
            return 0
        res = 0
        m, n = len(matrix), len(matrix[0])
        heights = [0] * n
        for i in range(m):
            for j in range(n):
                if matrix[i][j] == '0':
                    heights[j] = 0
                else:
                    heights[j] = heights[j] + 1
            res = max(res, self.largestRectangleArea(heights))
        return res
   
    def largestRectangleArea(self, heights):
        heights.append(0)
        stack = []
        res = 0
        for i in range(len(heights)):
            while stack and heights[i] < heights[stack[-1]]:
                s = stack.pop()
                res = max(res, heights[s] * ((i - stack[-1] - 1) if stack else i))
            stack.append(i)
        return res

单词搜索(79)

给定一个 m x n 二维字符网格 board 和一个字符串单词 word 。如果 word 存在于网格中,返回 true ;否则,返回 false

单词必须按照字母顺序,通过相邻的单元格内的字母构成,其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母不允许被重复使用。

例:输入:board = [["A","B","C","E"],["S","F","C","S"],["A","D","E","E"]], word = "ABCCED"

输出:true

class Solution:
    def exist(self, board: List[List[str]], word: str) -> bool:
        l,w = len(board),len(board[0])
        l_word = len(word)
        def dfs(a,b,index,hel):
            if a<0 or b<0 or a>=l or b>=w or board[a][b]!=word[index] or (a,b) in hel:
                return False
            elif board[a][b]==word[index] and index==len(word)-1:
                return True
            return dfs(a+1,b,index+1,hel+[(a,b)]) or dfs(a-1,b,index+1,hel+[(a,b)]) or dfs(a,b+1,index+1,hel+[(a,b)]) or dfs(a,b-1,index+1,hel+[(a,b)])
        
        for i in range(l):
            for j in range(w):
                if board[i][j] == word[0]:
                    if dfs(i,j,0,[]):
                        return True
        return False

岛屿数量

深度优先与广度优先都可以。DFS(深度优先搜索):

从一个为1的根节点开始访问,从每个相邻1节点向下访问到顶点(周围全是水),依次访问其他相邻1节点到顶点

BFS(广度优先搜索):从一个为1的根节点开始访问,每次向下访问一个节点,直到访问到最后一个顶点

class Solution:
    def numIslands(self, grid: List[List[str]]) -> int:
        if not grid or len(grid) == 'o': return 0
        row, columns = len(grid), len(grid[0])
        count = 0
        for i in range(row):
            for j in range(columns):
                if grid[i][j] == '1':
                    self.dfs(grid, i, j, row, columns)
                    count += 1
        return count

    def dfs(self, grid: List[List[str]], i: int, j: int, row: int, columns: int):
        if i >= row or i < 0 or j >= columns or j < 0 or grid[i][j] == '0': return
        grid[i][j] = '0'
        self.dfs(grid, i - 1, j, row, columns)
        self.dfs(grid, i, j - 1, row, columns)
        self.dfs(grid, i + 1, j, row, columns)
        self.dfs(grid, i, j + 1, row, columns)
岛屿最大面积
class Solution:
    def helper(self,grid,i,j):
        
        if i<0 or j<0 or i>=len(grid) or j>=len(grid[0]) or grid[i][j]==0:
            return 0
        grid[i][j]=0
        count=1
        tmp = [[1,0],[-1,0],[0,1],[0,-1]]
        for x,y in tmp:
            dx = x+i
            dy = y+j
            count+=self.helper(grid,dx,dy)
        
        return count
 
    def maxAreaOfIsland(self, grid: List[List[int]]) -> int:
        res = 0 
        for i in range(len(grid)):
            for j in range(len(grid[0])):
                if grid[i][j]==1:
                    
                    tmp = self.helper(grid,i,j)
                    res = max(tmp,res)
        return res
三角形路径之和(120)

给定一个三角形 triangle ,找出自顶向下的最小路径和。

每一步只能移动到下一行中相邻的结点上。相邻的结点 在这里指的是 下标 与 上一层结点下标 相同或者等于 上一层结点下标 + 1 的两个结点。也就是说,如果正位于当前行的下标 i ,那么下一步可以移动到下一行的下标 i 或 i + 1 。

例:输入:[[2],[3,4],[6,5,7],[4,1,8,3]]

输出:11,2--3--5--1

实例

class Solution:
    def minimumTotal(self, triangle: List[List[int]]) -> int:
      #自底向上
        dp = triangle
        m = len(triangle)
        
        for i in range(m-1,-1,-1):
            if i<m-1:
                for j in range(len(triangle[i])):
                    dp[i][j] += min(dp[i+1][j],dp[i+1][j+1])
        return dp[0][0]   

class Solution:
    def minimumTotal(self, triangle: List[List[int]]) -> int:
        #自底向上
        dp = triangle[-1]
        m = len(triangle)
        
        for i in range(m-1,-1,-1):
            if i<m-1:
                for j in range(len(triangle[i])):
                    dp[j] = min(dp[j],dp[j+1]) + triangle[i][j]
        return dp[0] 

顺时针打印数组(54,剑指offer29)

给你一个m*n的矩阵,请按照顺时针螺旋顺序,打印矩阵中的所有元素

如:输入:matrix = [[1,2,3],[4,5,6],[7,8,9]]

输出:[1,2,3,6,9,8,7,4,5]

思路: 取首行,去除首行后,对矩阵翻转来创建新的矩阵, 再递归直到新矩阵为[],退出并将取到的数据返回

class Solution:
    def spiralOrder(self, matrix: List[List[int]]) -> List[int]:
        ret = []
        if matrix == []:
            return ret
        ret.extend(matrix[0]) # 上侧
        new = [reversed(i) for i in matrix[1:]]
        if new == []:
            return ret
        r = self.spiralOrder([i for i in zip(*new)])
        ret.extend(r)
        return ret

js实现

var spiralOrder = function(matrix) {
    if(matrix.length === 0 ) return []
    let res = []
    // 分别定义 l:左边界,r:右边界,t:上边界,b:下边界
    let l = 0,r = matrix[0].length - 1, t = 0, b = matrix.length - 1
    // 为了让持续进入循环,通过内部判断超过边界值,来 break 跳出循环
    while(true){
        // 先从左至右,遍历第一行
        for(let i = l;i <= r;i++) res.push(matrix[t][i])
        // 同时将上边界+1,并判断当上边界超出下边界,则跳出循环
        if(++t > b) break
        // 从上至下,遍历最后一列
        for(let i = t;i <= b;i++) res.push(matrix[i][r])
        // 同时将右边界-1,并判断是否超出边界,超出则跳出循环
        if(--r < l) break
        // 从右至左,遍历最后一行
        for(let i = r;i >= l;i--) res.push(matrix[b][i])
        //同时将下边界-1 并判断是否超出上边界,超出跳出循环
        if(--b < t) break
        // 从左至右,遍历最左边一列
        for(let i = b;i >= t;i--) res.push(matrix[i][l])
        // 同时将左边界+1,并判断是否超出右边界,超出则跳出循环
        if(++l > r ) break
    }
    // 返回最终生成的数组 
    return res
}; spiralOrder([[1,2],[3,4]])

在给定的代码中,虽然 break 语句跳出了 while(true) 循环,但它并不会影响后面的 for 循环的执行。一旦 break 语句被触发,while 循环立即停止,但之后的代码仍然会按顺序执行。

在这种情况下,即使 while(true) 循环被中断,后面的 for 循环仍然会执行,因为程序的执行流程会继续向下执行,直到函数返回或者遇到其他控制流语句。

所以,即使 while(true) 循环被中断,后面的 for 循环仍然可以正常执行。

顺时针生成数组(59)

给你一个正整数 n ,生成一个包含 1n2 所有元素,且元素按顺时针顺序螺旋排列的 n x n 正方形矩阵 matrix

def generateMatrix(self,n: int) -> List[List[int]]:
  	ans = [[0]*n for _ in range(n)]
    op = itertools.cycle([(0,1),(1,0),(0,-1),(-1,0)])
    d = next(op)
    x,y = [0,0]
    for k = range(1,n**2+1):
      	ans[x][y] = k
        i,j = x+d[0], y+d[1]
        if not 0<= i < n or not 0<=j<n or ans[i][j]:
          	d = next(op)
            x,y = x+d[0], y+d[1]
        else:
          	x,y = i,j
    
    return ans

js

var generateMatrix = function(n) {
    // 生成一个二维的矩阵,往这个矩阵里面填充数据,最后返回矩阵即可
    const matrix = new Array(n).fill(0).map(() => new Array(n).fill(0))  
    
    let num = 1 // 填充的数据

    // 用上下左右四个指针去控制整个矩阵的填入
    let left=0, right=n-1, top=0, bottom=n-1;

    while(left < right && top < bottom){
        // 填充上层的列
        for(let col=left; col<right; col++){
            matrix[left][col] = num
            num++
        }

        // 填充右侧的行
        for(let row=top; row<bottom; row++){
            matrix[row][right] = num
            num++
        }

        // 填下层
        for(let col=right; col>left; col--){
            matrix[bottom][col] = num
            num++
        }

        // 填左层
        for(let row=bottom; row>top; row--){
            matrix[row][left] = num
            num++
        }

        left++
        right--
        top++
        bottom--
    }
    // 把方阵中心的那个值补上
    if(left==right && top==bottom){
        matrix[top][left] = num
    }

    return matrix
};

有序矩阵中第 K 小的元素378

给你一个 n x n 矩阵 matrix ,其中每行和每列元素均按升序排序,找到矩阵中第 k 小的元素。 请注意,它是 排序后 的第 k 小元素,而不是第 k 个 不同 的元素。

输入:matrix = [[1,5,9],[10,11,13],[12,13,15]], k = 8 输出:13 解释:矩阵中的元素为 [1,5,9,10,11,12,13,13,15],第 8 小元素是 13

class Solution:
    def kthSmallest(self, matrix: List[List[int]], k: int) -> int:
        lo, hi = matrix[0][0], matrix[-1][-1]
        while lo < hi:
            mid = (lo + hi) // 2
            if sum(bisect.bisect(row, mid) for row in matrix) < k:
                lo = mid + 1
            else:
                hi = mid
        return lo

生命游戏289

生命游戏,简称为生命,是英国数学家约翰·何顿·康威在1970年发明的细胞自动机。 // 给定一个包含 m × n 个格子的面板,每一个格子都可以看成是一个细胞。 // 每个细胞具有一个初始状态 live(1)即为活细胞, 或 dead(0)即为死细胞。每个细胞与其八个相邻位置(水平,垂直,对角线)的细胞都遵循以下四条生存定律: // // 如果活细胞周围八个位置的活细胞数少于两个,则该位置活细胞死亡; // 如果活细胞周围八个位置有两个或三个活细胞,则该位置活细胞仍然存活; // 如果活细胞周围八个位置有超过三个活细胞,则该位置活细胞死亡; // 如果死细胞周围正好有三个活细胞,则该位置死细胞复活; // 根据当前状态,写一个函数来计算面板上细胞的下一个(一次更新后的)状态。 // 下一个状态是通过将上述规则同时应用于当前状态下的每个细胞所形成的,其中细胞的出生和死亡是同时发生的。

输入: board = [ [1,1], [1,0]] 输出:[[1,1], [1,1]]

const gameOfLife = (board, generation = 1) => {
  const generationResult = [];
  for(let i=0;i<generation;i++) {
    let init;
    if(i === 0) {
      init = JSON.parse(JSON.stringify(board));
    } else {
      init = [...generationResult[i-1]]
    }
    for(let j = 0;j < board.length;j++) {
      for(let k =0;k < board[j].length;k++) {
				let neiborsLiveCount = 0;
        let neibors;
        neibors = [
          init[j-1]?.[k-1],init[j-1]?.[k],init[j-1]?.[k+1],
          init[j]?.[k-1],init[j]?.[k+1],
          init[j+1]?.[k-1],init[j+1]?.[k],init[j+1]?.[k+1],
        ]
        neibors.map((neibor) => {
          if(neibor === 1) {
						neiborsLiveCount++;
          }
        });
        
        if(neiborsLiveCount < 2 || neiborsLiveCount > 3 || (neiborsLiveCount === 2 && init[j][k] === 0)) {
          init[j][k] === 0
        } else {
          init[j][k] === 1
        }
      }
    }
    console.log(`generation${i}`, init);
    generationResult.push(init)
  }
}

旋转数组

搜索旋转排序数组目标值33、81

旋转有序数组是分段有序的,给你 旋转后 的数组 nums 和一个整数 target ,如果 nums 中存在这个目标值 target ,则返回它的索引,否则返回 -1

例:

输入:nums = [4,5,6,7,0,1,2], target = 0 输出:4

class Solution:
   def search(self,nums,target):
       lo,hi = 0,len(nums)-1
       while lo < hi:
          mid = (lo + hi)//2
          if(nums[0] > target) ^ (nums[0]>nums[mid]) ^ (target>nums[mid]):
             lo = mid + 1
          else 
             hi = mid
       return lo if lo == hi and target == nums[lo] else -1

81:如果数组中有重复元素,编写一个函数来判断给定的目标值是否存在于数组中。若存在返回 true,否则返回 false。

class Solution:
    def search(self, nums: List[int], target: int) -> bool:
        lo, hi = 0, len(nums)-1
        while lo <= hi:
            mid = (lo+hi) >> 1
            if nums[mid] == target:
                return True
            while lo<mid and nums[lo]==nums[mid]:
                lo += 1
            if nums[lo] <= nums[mid]: # 左侧有序
                if nums[lo] <= target < nums[mid]:
                    hi = mid - 1
                else:
                    lo = mid + 1
            else:                     # 右侧有序,因为下落点在左侧
                if nums[mid] < target <= nums[hi]:
                    lo = mid + 1
                else:
                    hi = mid - 1
        return False

搜索旋转排序数组最小值153、154

已知一个长度为 n 的数组,预先按照升序排列,经由 1 到 n 次 旋转 后,得到输入数组。例如,原数组 nums = [0,1,2,4,5,6,7] 在变化后可能得到: 若旋转 4 次,则可以得到 [4,5,6,7,0,1,2] 若旋转 7 次,则可以得到 [0,1,2,4,5,6,7] 注意,数组 [a[0], a[1], a[2], ..., a[n-1]] 旋转一次 的结果为数组 [a[n-1], a[0], a[1], a[2], ..., a[n-2]] 。

给你一个元素值 互不相同 的数组 nums ,它原来是一个升序排列的数组,并按上述情形进行了多次旋转。请你找出并返回数组中的 最小元素 。

class Solution: 
   def findMin(self,nums:List[int])->int:
       lo,hi=0,len(nums)-1
       while lo < li:
          mid = (lo+li)//2
          if(nums[mid]) > nums[hi]:
            lo = mid + 1
          elif nums[mid] == nums[hi]:
            hi -=1
          else 
            hi = mid
       return nums[lo]

如果数组中有重复元素,

class Solution:
    def findMin(self, nums: List[int]) -> int:
      	lo, hi = 0, len(nums)-1
        while lo < hi:
            mid = (lo+hi) >> 1
            if nums[mid] > nums[hi]:
                lo = mid + 1
            elif nums[mid] < nums[hi]:
                hi = mid
            else:
                hi -= 1
        return nums[lo]

旋转图像(48)

给定一个 n *× *n 的二维矩阵表示一个图像。

将图像顺时针旋转 90 度

给定 matrix = 
[
  [1,2,3],
  [4,5,6],
  [7,8,9]
],

原地旋转输入矩阵,使其变为:
[
  [7,4,1],
  [8,5,2],
  [9,6,3]
]

js

function rotate(ary) {
  let vector = ary.length
  // 垂直替换
  debugger
  for (let i = 0, len = vector / 2; i < len; i++) {
    for (let j = 0; j < vector; j++) {
      // 以中心轴进行交换
      let cur = ary[i][j]
      // ary[vector - i - 1][j] 数组长度 - 当前循环列 - 1 (在减一因为索引是从 0 开始的)
      ary[i][j] = ary[vector - i - 1][j]
      ary[vector - i - 1][j] = cur
    }        
  }
  // 以对角线进行交换
  for (let i = 0; i < vector; i++) {
    for (let j = 0; j < i; j++) {
      let cur = ary[i][j]
      // ary[i][j] 和 ary[j][i],因为 j 会比 i 小一个数,所有获取时调换 位置,可以让数组斜对角交换
      ary[i][j] = ary[j][i]
      ary[j][i] = cur
    }        
  }
  return ary
}

console.log(rotate([
  [1,2,3],
  [4,5,6],
  [7,8,9]
]))
console.log(rotate([
  [15,13, 2, 5],
  [14, 3, 4, 1],
  [12, 6, 8, 9],
  [16, 7,10,11]
]))
如果你觉得我的文章对你有帮助的话,希望可以推荐和交流一下。欢迎關注和 Star 本博客或者关注我的 Github