算法技巧_(单链表&数组)

算法技巧(单链表&数组)

常用的数据结构

  1. 数组: 查询快、插入、删除慢 时间复杂度O(n)
  2. 链表:查询慢、插入、删除快 时间复杂度O(n)
  3. 树: 链表的进化 时间复杂度O(logN) 代表有二叉平衡树 红黑树 B树 B+树
    1. 遍历方式
      1. 先序遍历 (根序遍历) 根左右(NLR)
      2. 中序遍历 (中根遍历) 左根右(LNR)
      3. 后序遍历 (后跟遍历) 左右根 (LRN)
    2. 多叉树是无中序遍历的,因为二叉树只有两个节点 多叉树有很多个节点
  4. 堆: 有序 插入删除效率高 时间复杂度O(logN) 可以有效解决topN 问题

    JAVA 中的优先级队列(最大堆,最小堆)指定排序顺序规则;有帮助其topN问题 典型应用 合并K个有序列表

  5. 图:

常用简单算法技巧

  1. 左右指针:常用于链表,数组的算法,相向而行 或者相背而行一次遍历 比如合并两个链表;
  2. 快慢指针:判断链表是否有环;寻找链表的中间节点;查找链表倒数第N个节点;删除链表倒数第N个节点;
  3. 二分搜索技巧
  4. 滑动窗口:快慢指针的变形 快慢指针之间就是有效窗口
  5. 动态规划
  6. 回溯
  7. 分治
  8. BFS

单链表常见题目及解题思路

虚拟节点的概念:在产生一条新的链表之前 可以先初始化一个虚拟节点,避免发生空指针异常;完整链表可以取虚拟链表的下个节点。

  1. 合并两个有序链表
    • 解题思路:
      1. 创造一条新的链表
      2. 双指针(p1,p2) 开始循环两个链表 循环结束条件p1和p2指针所指节点都不为空。
      3. 循环体内判断p1和p2指针所指节点大小,谁小谁就放到新的链表的位置p的下个节点,并且此链表前进一步;并且p指针前进一步。
      4. 循环结束后判断p1和p2是否为空,如果存在那么也就是有一条链表为循环完,那么p的next就指向此链表当前位置的节点。此刻一个完整的链表拼接完成了。
  2. 单链表拆解
    • 解题思路:
      1. 构造两个链表A B
      2. 循环单链表 小于X的放到A 大于等于X的放到B (每次放节点需要断开此节点的next指针)
      3. 拼接A和B两个链表 组合成前小于X和大于X的链表
  3. 合并K个有序链表
    • 解题思路
      • 创造一条新的链表
      • 构造一个大小为K的最小堆那么堆节点就是就是最K个链表中最小元素
      • 将全部链表的头节点压入的构造的最小堆,只要堆不为空就弹出堆顶元素。
      • 堆顶元素出队后,放到新链表指针P出,指针P并且向前一步;堆顶元素下个元素再入堆,然后再弹出堆顶元素,直到所有堆内元素弹出完毕
  4. 获取单链表的倒数k个节点
    • 解题思路一:
      • 遍历一遍链表知道链表节点N
      • 遍历第二遍N-K就是要的倒数k个节点
    • 解题思路二:
      • 快慢指针 一次遍历;
      • 快指针先走K步,慢节点在开始,快指针结束之时 慢指针就是倒数K个节点。
  5. 单链表的中点
    • 解题思路:
      • 快慢指针 一次遍历
      • 快指针先走2两步,慢指针走1步,快指针结束之时 慢指针就是中点。
  6. 判断链表是否有环
    • 解题思路:
      • 快慢指针 一次遍历
      • 快指针先走2两步,慢指针走1步,快指针只有存在节点就一直循环,比较快慢指针值是否相等,如何相等则有环。
  7. 两个链表是否相交
    • 解题思路一:
      • 双指针 让双指针同时到达相交点
      • 两次遍历
      • 拼接链表A+B 和 链表B+A
      • 同时开始循环,相等节点就是相交点
    • 解题思路二:
      • 存一个hashSet 然后再循环另一个链表,判断是否存在,存在就是相交。

数组常见题目及解题思路

  1. 删除有序数组中的重复元素并按最初的元素进行排列
    • 解题思路:
      • 采用快慢指针
      • 同时从起点开始,判断fast!=slow 那么 slow++ 慢指针位置为快指针问题
      • 当fast移动到数组尾部,那么[0,slow]就是要求的有序数组。
    • 相似题目:
      • 删除链表中中的重复元素
      • 解题思路:
        • 采用快慢指针,判断fast!=slow 那么slow.next = fast ;slow=slow.next;fast=fast.next
        • 最后断开slow.next = null
  2. 给定一个数组nums和一个值val 需要原地移除所有数值等于val的元素,并返回移除后数组的长度。
    • 解题思路:

      • 快慢指针
      • slow和fast指针都指向开始节点
      • 循环fast,判断fast!=val 那么nums[slow] = nums[fast] slow++;
      • 有效区间是[0,slow-1] 因为slow最后会++ 向前一步

      与删除重复元素不同点,先赋值在移动slow指针

  3. 输入一个数组,请你原地修改,将数组中所有值为0的元素移动到数组的末尾
    • 解题思路:
      • 类似题目2;移除所有数值等于val的元素,最后将[slow,fast]区间的都赋值为0
  4. 求两数之和 给你一个下标从 1 开始的整数数组 ,该数组已按 非递减顺序排列 ,请你从数组中找出满足相加之和等于目标数 target 的两个数。如果设这两个数分别是 numbers[index1] 和 numbers[index2] ,则 1 <= index1 < index2 <= numbers.length
    • 解题思路:
      • 左右指针相向而行 + 二分搜索
      • while 循环条件 left < right
      • 判断int sum = num[left] + num[right]
      • 如果相等返回 return new int[]{left + 1, right + 1};
      • 如果 sum>target right--
      • 如果 sum<target left++
      • 否则返回 return new int[]{-1, -1};
  5. 反转数组
    • 解题思路:
      • 左右指针相向而行
      • while 循环条件 left < right
      • 借助temp变量 交换left 和right
  6. 回文串判断
    • 解题思路
      • 左右指针相向而行
      • while 循环条件 left < right
      • 如果存在s[left]!=s[right]返回false
      • 否则left++ rigth--
      • 默认返回true
  7. 最长回文子串
    • 解题思路
      • 先解决局部问题
      • 回文子串要不是奇数 要不是偶数
      • 双指针从中心向两侧移动
      • 奇数:左右指针在同一个字符串位置
      • 偶数:左右指针在相邻位置
      • while 条件 left>0 && right<s.lenth && s.charAt(left) == s.charAt(right);循环体内 left-- right++;
      • 返回s.substring(left+1,right)
      • 遍历整个字符串,分别调用局部函数 获取奇数,偶数的回文子串 并且记录谁最长,并最后返回。
posted @ 2024-04-12 14:42  贺艳峰  阅读(7)  评论(0编辑  收藏  举报