有序的数组,试试用指针法遍历

简介: 有序的数组,试试用指针法遍历

有序的数组,试试用指针法遍历


最近在看数据结构和算法,努力总结出道~

TL:DR

指针的本质,是记住数组遍历的进度,从而减少无效遍历的范围。

当遍历有序的数组的时候,因为有序,所以更大更小的值已经确定,可以用指针法记住数组遍历的进度,从而减少无效遍历的范围

数组上,单个指针的话,先将指针指向开始(或末尾),然后指针移动,当移动到数组之外,就表示数组遍历完毕。

数组上,两个指针的话,如果出现在两端,随着两边指针移动,未遍历中间的元素范围就慢慢变小,直到指针相邻或者重合表示,遍历完毕,也叫对撞指针法

遇到求和或者比大小问题的时候,如果数组是无序的,可以尝试有序之后再使用指针法。

练习:有序数组合并

网络异常,图片无法展示
|

我看到的第一想法是sort大法:

var merge = function (nums1, m, nums2, n) {
  // num1里面多余的元素删掉,并且将num2里面的元素插入到num1里面
  nums1.splice(m,n,...nums2.slice(0,n))
  // 排序
  nums1.sort((a,b)=>a-b)
};

splice方法一定要会,可删,可增。第一个索引表示要操作的位置(这个位置的元素肯定要变),第二个表示删除后面几个,之后的都表示插入的元素。

但其实,既然已经有序,就要充分利用这个条件,减少无效遍历。上指针大法!

本题,因为是num1够长,因为后面的空间是空的,所以从后面开始利用,节省空间。

  • nums1、nums2本身有序,所以里面的数也是从小到大排列,也就是最大的数始终在末尾
  • 将大的数,倒插进num1就好
  • 一个指针,指向num1的最后一个数的位置。一个指向num2的最后一个数的位置。还用个指针指向填充到num1的哪个位置了
  • 只要指针还在,就一直比较,前两个指针的数,大的往后填,且指针移动
  • 指针1小于0的时候,表示num1遍历完了,那直接将num2里面,未遍历的数挨个直接塞到num1里就好
  • 指针2小于0的时候,表示num2遍历完了,那就不用管了,数都在num1里面了
var merge = function (nums1, m, nums2, n) {
  // 指针初始都在末尾
  let pointer1 = m - 1;
  let pointer2 = n - 1;
  let fillPointer = m + n - 1;
  // 一直遍历,直到有一个数组遍历完,一个数组遍历完的表现就是:pointer1 <0 ||  pointer2 <0
  while (pointer1 >= 0 && pointer2 >= 0) {
    // num1指针的指向的值更大的话,就填充此值,然后移动指针,表示此值之后不再需要遍历了
    if (nums1[pointer1] >= nums2[pointer2]) {
      nums1[fillPointer] = nums1[pointer1];
      pointer1--;
      fillPointer--;
    } else {
      // 同理
      nums1[fillPointer] = nums2[pointer2];
      pointer2--;
      fillPointer--;
    }
  }
  // 当nums1先遍历完的时候,将nums2里面未遍历的值,插入到nums1即可
  if (pointer1 < 0) {
    // pointer2 >=0是遍历的条件,一旦小于0就表示遍历完了
    while (pointer2 >=0) {
      nums1[newPointer] = nums2[pointer2];
      pointer2--;
      newPointer--;
    }
  }
};

显然空间O(1),时间O(m+n)

可以看下官方视频

练习:三数求和

网络异常,图片无法展示
|

暴力法,我就不说了,三重遍历,合适的组合丢出来就行,时间复杂度O(n^3)。

显然,这不是想要的过程。

两数求和,用Map,以空间换时间。 但三数求和,不固定的数有两个,是不适合用Map的,这里数组和求和,联想下指针。

指针的前提条件是有序的数组,所以这里先排序,然后使用指针法

  • 数组先排序
  • 固定一个数nums[i],再使用左右指针指向 nums[i]后面的两端
  • 判断三数之和,大于0右指针后退,小于0左指针前进,等于0保存索引且左右指针都动
  • nums[i]如果大于0,则停止遍历,因为之后的肯定更大于0
  • 对于重复的问题:左右指针以及i,只要和前面的值一致则跳过。
var threeSum = function (nums) {
  // nums = Array.from(new Set(nums));
  nums.sort((a, b) => a - b);
  let res = [];
  let len = nums.length;
  // i是固定的第一个数的指针
  for (let i = 0; i < len - 2; i++) {
    if (nums[i] > 0) {
      break;
    }
    // 相等就跳过,因为有i-1所以前面必须判断i>0,不然会报错
    if (i > 0 && nums[i] === nums[i - 1]) {
      continue;
    }
    // L是左指针
    let L = i + 1;
    // R是右指针
    let R = len - 1;
    // 当L>=R的时候,表示数组遍历完了
    while (L < R) {
      const sum = nums[L] + nums[R] + nums[i];
      // 大于0,右指针后退
      if (sum > 0) {
        // 这句是去重复的。值相同的话,跳过,这里注意必须加L<R,不然R--,是有可能小于L的,这不是我们想要的
        while (L < R && nums[R] === nums[R - 1]) R--;
        R--;
      } else if (sum < 0) {
        while (L < R && nums[L] === nums[L + 1]) L++;
        L++;
      } else {
        res.push([nums[i], nums[L], nums[R]]);
        while (L < R && nums[L] === nums[L + 1]) L++;
        L++;
        while (L < R && nums[R] === nums[R - 1]) R--;
        R--;
      }
    }
  }
  return res;
};

时间复杂度降成O(n^2)。

可以看下官方视频

引用

目录
相关文章
|
2月前
使用指针访问数组元素
【10月更文挑战第30天】使用指针访问数组元素。
39 3
|
2月前
|
存储 程序员 编译器
C 语言数组与指针的深度剖析与应用
在C语言中,数组与指针是核心概念,二者既独立又紧密相连。数组是在连续内存中存储相同类型数据的结构,而指针则存储内存地址,二者结合可在数据处理、函数传参等方面发挥巨大作用。掌握它们的特性和关系,对于优化程序性能、灵活处理数据结构至关重要。
|
2月前
|
存储 C语言 计算机视觉
在C语言中指针数组和数组指针在动态内存分配中的应用
在C语言中,指针数组和数组指针均可用于动态内存分配。指针数组是数组的每个元素都是指针,可用于指向多个动态分配的内存块;数组指针则指向一个数组,可动态分配和管理大型数据结构。两者结合使用,灵活高效地管理内存。
|
2月前
|
容器
在使用指针数组进行动态内存分配时,如何避免内存泄漏
在使用指针数组进行动态内存分配时,避免内存泄漏的关键在于确保每个分配的内存块都能被正确释放。具体做法包括:1. 分配后立即检查是否成功;2. 使用完成后及时释放内存;3. 避免重复释放同一内存地址;4. 尽量使用智能指针或容器类管理内存。
|
2月前
|
存储 NoSQL 编译器
C 语言中指针数组与数组指针的辨析与应用
在C语言中,指针数组和数组指针是两个容易混淆但用途不同的概念。指针数组是一个数组,其元素是指针类型;而数组指针是指向数组的指针。两者在声明、使用及内存布局上各有特点,正确理解它们有助于更高效地编程。
|
2月前
|
存储 人工智能 算法
数据结构实验之C 语言的函数数组指针结构体知识
本实验旨在复习C语言中的函数、数组、指针、结构体与共用体等核心概念,并通过具体编程任务加深理解。任务包括输出100以内所有素数、逆序排列一维数组、查找二维数组中的鞍点、利用指针输出二维数组元素,以及使用结构体和共用体处理教师与学生信息。每个任务不仅强化了基本语法的应用,还涉及到了算法逻辑的设计与优化。实验结果显示,学生能够有效掌握并运用这些知识完成指定任务。
60 4
|
2月前
使用指针访问数组元素
【10月更文挑战第31天】使用指针访问数组元素。
51 2
|
2月前
|
存储 C语言
C语言如何使用结构体和指针来操作动态分配的内存
在C语言中,通过定义结构体并使用指向该结构体的指针,可以对动态分配的内存进行操作。首先利用 `malloc` 或 `calloc` 分配内存,然后通过指针访问和修改结构体成员,最后用 `free` 释放内存,实现资源的有效管理。
151 13
|
3月前
|
C语言
无头链表二级指针方式实现(C语言描述)
本文介绍了如何在C语言中使用二级指针实现无头链表,并提供了创建节点、插入、删除、查找、销毁链表等操作的函数实现,以及一个示例程序来演示这些操作。
41 0
|
4月前
|
存储 人工智能 C语言
C语言程序设计核心详解 第八章 指针超详细讲解_指针变量_二维数组指针_指向字符串指针
本文详细讲解了C语言中的指针,包括指针变量的定义与引用、指向数组及字符串的指针变量等。首先介绍了指针变量的基本概念和定义格式,随后通过多个示例展示了如何使用指针变量来操作普通变量、数组和字符串。文章还深入探讨了指向函数的指针变量以及指针数组的概念,并解释了空指针的意义和使用场景。通过丰富的代码示例和图形化展示,帮助读者更好地理解和掌握C语言中的指针知识。
155 4