算法技巧_(单链表&数组)
算法技巧(单链表&数组)
常用的数据结构
- 数组: 查询快、插入、删除慢 时间复杂度O(n)
- 链表:查询慢、插入、删除快 时间复杂度O(n)
- 树: 链表的进化 时间复杂度O(logN) 代表有二叉平衡树 红黑树 B树 B+树
- 遍历方式
- 先序遍历 (根序遍历) 根左右(NLR)
- 中序遍历 (中根遍历) 左根右(LNR)
- 后序遍历 (后跟遍历) 左右根 (LRN)
- 多叉树是无中序遍历的,因为二叉树只有两个节点 多叉树有很多个节点
- 遍历方式
- 堆: 有序 插入删除效率高 时间复杂度O(logN) 可以有效解决topN 问题
JAVA 中的优先级队列(最大堆,最小堆)指定排序顺序规则;有帮助其topN问题 典型应用 合并K个有序列表
- 图:
常用简单算法技巧
- 左右指针:常用于链表,数组的算法,相向而行 或者相背而行一次遍历 比如合并两个链表;
- 快慢指针:判断链表是否有环;寻找链表的中间节点;查找链表倒数第N个节点;删除链表倒数第N个节点;
- 二分搜索技巧
- 滑动窗口:快慢指针的变形 快慢指针之间就是有效窗口
- 动态规划
- 回溯
- 分治
- BFS
单链表常见题目及解题思路
虚拟节点的概念:在产生一条新的链表之前 可以先初始化一个虚拟节点,避免发生空指针异常;完整链表可以取虚拟链表的下个节点。
- 合并两个有序链表
- 解题思路:
- 创造一条新的链表
- 双指针(p1,p2) 开始循环两个链表 循环结束条件p1和p2指针所指节点都不为空。
- 循环体内判断p1和p2指针所指节点大小,谁小谁就放到新的链表的位置p的下个节点,并且此链表前进一步;并且p指针前进一步。
- 循环结束后判断p1和p2是否为空,如果存在那么也就是有一条链表为循环完,那么p的next就指向此链表当前位置的节点。此刻一个完整的链表拼接完成了。
- 解题思路:
- 单链表拆解
- 解题思路:
- 构造两个链表A B
- 循环单链表 小于X的放到A 大于等于X的放到B (每次放节点需要断开此节点的next指针)
- 拼接A和B两个链表 组合成前小于X和大于X的链表
- 解题思路:
- 合并K个有序链表
- 解题思路
- 创造一条新的链表
- 构造一个大小为K的最小堆那么堆节点就是就是最K个链表中最小元素
- 将全部链表的头节点压入的构造的最小堆,只要堆不为空就弹出堆顶元素。
- 堆顶元素出队后,放到新链表指针P出,指针P并且向前一步;堆顶元素下个元素再入堆,然后再弹出堆顶元素,直到所有堆内元素弹出完毕
- 解题思路
- 获取单链表的倒数k个节点
- 解题思路一:
- 遍历一遍链表知道链表节点N
- 遍历第二遍N-K就是要的倒数k个节点
- 解题思路二:
- 快慢指针 一次遍历;
- 快指针先走K步,慢节点在开始,快指针结束之时 慢指针就是倒数K个节点。
- 解题思路一:
- 单链表的中点
- 解题思路:
- 快慢指针 一次遍历
- 快指针先走2两步,慢指针走1步,快指针结束之时 慢指针就是中点。
- 解题思路:
- 判断链表是否有环
- 解题思路:
- 快慢指针 一次遍历
- 快指针先走2两步,慢指针走1步,快指针只有存在节点就一直循环,比较快慢指针值是否相等,如何相等则有环。
- 解题思路:
- 两个链表是否相交
- 解题思路一:
- 双指针 让双指针同时到达相交点
- 两次遍历
- 拼接链表A+B 和 链表B+A
- 同时开始循环,相等节点就是相交点
- 解题思路二:
- 存一个hashSet 然后再循环另一个链表,判断是否存在,存在就是相交。
- 解题思路一:
数组常见题目及解题思路
- 删除有序数组中的重复元素并按最初的元素进行排列
- 解题思路:
- 采用快慢指针
- 同时从起点开始,判断fast!=slow 那么 slow++ 慢指针位置为快指针问题
- 当fast移动到数组尾部,那么[0,slow]就是要求的有序数组。
- 相似题目:
- 删除链表中中的重复元素
- 解题思路:
- 采用快慢指针,判断fast!=slow 那么slow.next = fast ;slow=slow.next;fast=fast.next
- 最后断开slow.next = null
- 解题思路:
- 给定一个数组nums和一个值val 需要原地移除所有数值等于val的元素,并返回移除后数组的长度。
-
解题思路:
- 快慢指针
- slow和fast指针都指向开始节点
- 循环fast,判断fast!=val 那么nums[slow] = nums[fast] slow++;
- 有效区间是[0,slow-1] 因为slow最后会++ 向前一步
与删除重复元素不同点,先赋值在移动slow指针
-
- 输入一个数组,请你原地修改,将数组中所有值为0的元素移动到数组的末尾
- 解题思路:
- 类似题目2;移除所有数值等于val的元素,最后将[slow,fast]区间的都赋值为0
- 解题思路:
- 求两数之和 给你一个下标从 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};
- 解题思路:
- 反转数组
- 解题思路:
- 左右指针相向而行
- while 循环条件 left < right
- 借助temp变量 交换left 和right
- 解题思路:
- 回文串判断
- 解题思路
- 左右指针相向而行
- while 循环条件 left < right
- 如果存在s[left]!=s[right]返回false
- 否则left++ rigth--
- 默认返回true
- 解题思路
- 最长回文子串
- 解题思路
- 先解决局部问题
- 回文子串要不是奇数 要不是偶数
- 双指针从中心向两侧移动
- 奇数:左右指针在同一个字符串位置
- 偶数:左右指针在相邻位置
- while 条件 left>0 && right<s.lenth && s.charAt(left) == s.charAt(right);循环体内 left-- right++;
- 返回s.substring(left+1,right)
- 遍历整个字符串,分别调用局部函数 获取奇数,偶数的回文子串 并且记录谁最长,并最后返回。
- 解题思路