链表和二叉树篇
文章目录
前言
本次算法入门训练主要训练以下二个内容:
- 以特殊数据结构为载体的算法结构,比如:数组、链表、栈、二叉树等
- 以考察常见算法思想基础的算法题,比如:动态规划、贪心、回溯等
- 基于某种场景包装下的1和2
提示:以下是本篇文章正文内容
内容
第一题:给定一个单链表的头结点pHead(该头节点是有值的,比如在下图,它的val是1),长度为n,反转该链表后,返回新链表的表头。
解题思路:有两种常规解法。1.用三个指针来确定节点的位置,防止反转过程中断链的情况,非常简单,只不过要考虑几种特殊情况,链表的题一般来说都要考虑很多情况。2.头插法,设置一个新节点,遍历原链表每次以头插的形式插入到新节点后。
OJ链接:反转链表
public class Solution {
public ListNode ReverseList(ListNode head) {
//三个指针法
//链表为空和链表只有一个节点的情况
if(head == null || head.next == null){
return head;
}
ListNode prev = head;
ListNode cur = head.next;
ListNode last = cur.next;
while(last != null){
//反转逻辑
cur.next = prev;
prev = cur;
cur = last;
last = last.next;
}
//链表中只有两个节点和最后两个节点的反转
cur.next = prev;
//最后处理现在的尾节点
head.next = null;
return cur;
}
}
public class Solution {
public ListNode ReverseList(ListNode head) {
//头插法
if (head == null || head.next == null) {
return head;
}
ListNode new_head = null;
while (head != null) {
//先从原链表中去掉第一个节点
ListNode p = head;
head = head.next;
//再将p标识的节点头查到新链表
p.next = new_head;
new_head = p;
}
head = new_head;
return head;
}
}
第二题:输入两个单调递增的链表,输出两个链表合成后的链表,当然我们需要合成后的链表满足单调不减规则。
解题思路:1.设置一个头节点,用做合并新链表的头节点,两个链表挨个挨个比大小,小的接到新链表当中。2.当然也可以利用递归来一个一个的归并。
OJ链接:链表合并
public class Solution {
public ListNode Merge(ListNode list1,ListNode list2) {
//两个链表两个头节点,挨个比大小,小的接到新链表中
//处理某个链表为空或者都为空的情况
if(list1 == null){
return list2;
}
if(list2 == null){
return list1;
}
ListNode list = new ListNode(-1);
ListNode ret = list;
while(list1 != null && list2 !=null){
if(list1.val > list2.val){
//判断接哪个链表
list.next = list2;
list = list.next;
list2 = list2.next;
}else{
list.next = list1;
list = list.next;
list1 = list1.next;
}
}
//处理有链表没接完的情况
if(list1 != null){
list.next = list1;
}
if(list2 != null){
list.next = list2;
}
return ret.next;
}
}
public class Solution {
public ListNode Merge(ListNode list1,ListNode list2) {
//递归
//递归结束条件
if(list1 == null){
return list2;
}
if(list2 == null){
return list1;
}
//合并中,找到第一个节点
ListNode ret = null;
if(list1.val < list2.val){
ret = list1;
list1 = list1.next;
}else{
ret = list2;
list2 = list2.next;
}
//合并
ret.next = Merge(list1,list2);
return ret;
}
}
第三题:输入两棵二叉树A,B,判断B是不是A的子结构。(ps:我们约定空树不是任意一个树的子结构)
解题思路:递归,因为二叉树都是递归定义的所以递归操作是常见操作。比较过程分为两步,1.先确定起始位置是否符合。2.再从该位置开始判断其左右子树是否符合
OJ链接:树的子结构
public class Solution {
private boolean CheckChild(TreeNode root1, TreeNode root2) {
//代表全部都符合
if (root2 == null ) {
return true;
}
//主树走完了但是子树还没有走完,root2 != null
if(root1 == null){
return false;
}
//子树节点不相等
if(root1.val != root2.val){
return false;
}
//深度递归
return CheckChild(root1.left,root2.left) && CheckChild(root1.right,root2.right);
}
public boolean HasSubtree(TreeNode root1, TreeNode root2) {
//递归
if (root1 == null || root2 == null) {
return false;
}
boolean flag = false;
//确定头节点
if (root1.val == root2.val) {
//查看左子树,右子树是否符合
flag = CheckChild(root1,root2);
}
//当前头节点不符合,递归左子树右子树
if(flag != true){
flag = HasSubtree(root1.left,root2);
}
if(flag != true){
flag = HasSubtree(root1.right,root2);
}
return flag;
}
}
第四题:操作给定的二叉树,将其变换为源二叉树的镜像
解题思路:根据二叉树构建的特性递归就可以完成,只不过要根据题目意思每次递归都要旋转
OJ链接:镜像二叉树
class Solution {
public TreeNode invertTree(TreeNode root) {
if(root == null) return null;
TreeNode tmp = root.left;
root.left = root.right;
root.right = tmp;
invertTree(root.left);
invertTree(root.right);
return root;
}
}
第五题:在一个排序的链表中,存在重复的结点,请删除该链表中重复的结点,重复的结点不保留,返回链表头指针。 例如,链表1->2->3->3->4->4->5 处理后为 1->2->5
解题思路:这题很容易想到用两个指针移动删除,但是要考虑很多情况,1.没有重复节点,2.有部分重复节点,部分重复节点直至最后一个节点。3.全部都是重复节点。要把这几种情况全都考虑进去,代码就很简单了。
OJ链接:删除链表中重复的节点
/*
public class ListNode {
int val;
ListNode next = null;
ListNode(int val) {
this.val = val;
}
}
*/
public class Solution {
public ListNode deleteDuplication(ListNode pHead) {
if(pHead == null){
return pHead;
}
//创建一个虚拟头节点是为了处理链表所有节点都是重复节点
ListNode head = new ListNode(-1);
head.next = pHead;
ListNode prev = head;
ListNode last = prev.next;
while(last != null){
//没有重复节点
while(last.next != null && last.val != last.next.val){
prev = prev.next;
last = last.next;
}
//有重复节点
while(last.next != null && last.val == last.next.val){
last = last.next;
}
//说明有重复节点
if(prev.next != last){
prev.next = last.next;
}
last = last.next;
}
return head.next;
}
}
总结
以上就是今天训练的内容,对于链表的题来说总是有很多特殊的情况需要考虑,但是思路是很简单的,而二叉树的题多是根据递归来围绕展开,对于某些应用场景我们也要学会向着二叉树来转换然后进行思考。