大傻子复习之前写的代码 蠢蛋
逆转链表递归就写了一小时,真的不知道之前自己怎么写的题目
package offer;
import java.util.*;
/*
复习链表+树的习题+力扣相关习题刷
1链表从尾到头打印链表
2逆转链表 三种方法 递归 栈 双指针
3删除表中重复的节点
4链表中倒数第k个节点
5合并两个排序的链表
6两个链表的第一个公共节点
*/
class Nodes {
int val;
Nodes next;
public Nodes(int val) {
this.val = val;
}
public Nodes(int val, Nodes next) {
this.val = val;
this.next = next;
}
//打印
public void print() {
Nodes cur = this;
while (cur != null) {
System.out.print(cur.val + " ");
cur = cur.next;
}
}
}
public class Review {
//逆序打印链表
public static void reversePrint(Nodes node) {
//递归上一层出口
if (node == null) return;
reversePrint(node.next);
System.out.print(node.val + " ");
}
//逆转链表
/**
* 递归逆转链表
* 找到倒数第二个把指针逆转
* 返回一个新链表 创建新空间
*
* @param head
*/
public static Nodes transNodes(Nodes head) {
// Nodes newhead = null;
//情况 只有一个节点或者节点为空 递归返回条件
if (head == null || head.next == null) return head;
//递归往下走 找到倒数第二个节点
//为什么head4 cur 5
Nodes cur = transNodes(head.next);
head.next.next = head;
//最后一个指向空
head.next = null;
return cur;
}
/**
* 非递归写反转链表
*
* @param head
* @return
*/
public static Nodes transNodes2(Nodes head) {
//有一个或者为空时候
if (head == null || head.next == null) return head;
Nodes pre = null;
Nodes cur = head;
//建立一个零时变量
Nodes tmp = null;
while (cur != null) {
tmp = cur.next;
cur.next = pre;
pre = cur;
cur = tmp;
}
//要从第一个节点返回
return pre;
}
/**
* 删除表中所有重复的节点
* 链表有序 1222345
* 下一个节点的数值与上一个相同
* 首先找到重复的再删除 删除一个留一个
*
* @param head
*/
public static Nodes deleteSame(Nodes head) {
if (head == null || head.next == null) return head;
//简便的方法 只用一个指针
Nodes cur = head;
//边界条件cur != null && cur.next != null
while (cur.next != null) {
//前后数值的值相等
if (cur.val == cur.next.val) {
//删除相等的元素
cur.next = cur.next.next;
} else {
cur = cur.next;
}
}
return head;
}
/**
* 删除倒数第K个节点
* 两个指针 一个先走k个节点 然后再一起走 一个到了末尾 另一个就是第K个节点返回
* 快慢指针
*
* @param head
* @return
*/
public static Nodes deleteK(Nodes head, int k) {
if (head == null || k < 0) return head;
Nodes fast = head;
Nodes slow = head;
//p1先走K个节点
for (int i = 0; i < k; i++) {
//没有走到最后一个
if (fast != null)
fast = fast.next;
else return null;
}
//两个一起走
while (fast != null) {
slow = slow.next;
fast = fast.next;
}
return slow;
}
/**
* 合并两个排序链表
* 判断情况 1两个链表为空 2一个为空一个不为空 3 两个都不为空
* 不为空的情况下 比较数值进行插入
* 1 2 3 4 5
* 4 5 6 7 8
*
* @param nodesA
* @param nodesB
* @return
*/
public static Nodes mergeNodes(Nodes nodesA, Nodes nodesB) {
//为空的三种情况
if (nodesA == null && nodesB == null) return null;
if (nodesA == null) return nodesB;
if (nodesB == null) return nodesA;
//新联表设置头结点
Nodes nhead = new Nodes(-1);
//设置一个可以移动的指针
Nodes newhead = nhead;
//两个都存在的情况
while (nodesA != null && nodesB != null) {
if (nodesA.val <= nodesB.val) {
newhead.next = nodesA;
nodesA = nodesA.next;
} else {
newhead.next = nodesB;
nodesB = nodesB.next;
}
newhead = newhead.next;
}
//多出的节点 如果A还多余 把后面的数值全部放上
if (nodesA != null)
newhead.next = nodesA;
if (nodesB != null)
newhead.next = nodesB;
return nhead.next;
}
/**
* 从尾节点开始遍历 后进先出 找到第一个不相等的元素弹出
* 链表结点值A 123467 B 467
* 栈A 764321 栈B 764 相同则退栈764 commo存储最后一个相等的元素
* 时间空间O(n+m)
*
* @param pHead1
* @param pHead2
*/
public static Nodes findFirstSame(Nodes pHead1, Nodes pHead2) {
// 异常
if (pHead1 == null || pHead2 == null) {
return null;
}
// JDK建议双向队列Deque优先于Stack
Deque<Nodes> s1 = new ArrayDeque<>();
Deque<Nodes> s2 = new ArrayDeque<>();
Nodes commonNode = null;
// 链表入栈
while (pHead1 != null) {
s1.push(pHead1);
pHead1 = pHead1.next;
}
while (pHead2 != null) {
s2.push(pHead2);
pHead2 = pHead2.next;
}
// 逐个弹出栈顶元素比较,commonNode储存同节点
while (!s1.isEmpty() && !s2.isEmpty()) {
if (s1.peek().val == s2.peek().val) {
commonNode = s1.peek();
}
s1.pop();
s2.pop();
}
//
return commonNode;
}
/**
* 齐头并进 相同位置进行后移
*
* @param pHead1
* @param pHead2
* @return
*/
public static Nodes findFirstSame2(Nodes nodesA, Nodes nodesB) {
//改进写的很乱也很杂 也很蠢
//进行判空
if (nodesA == null || nodesB == null) return null;
//用指针来进行
Nodes pHead1 = nodesA;
Nodes pHead2 = nodesB;
//求两个链表长度 计算长度写成函数不会更改当前的链表指针位置 不然会报错
int length1 = getLength(nodesA);
int length2 = getLength(nodesB);
//移动差值 判断哪个是正数 1长1先遍历
if (length1 >= length2) {
int len = length1 - length2;
while (len > 0) {
pHead1 = pHead1.next;
len--;
}
} else if (length1 < length2) {
int len = length2 - length1;
while (len > 0) {
pHead2 = pHead2.next;
len--;
}
}
//舍去了长的位置开始一起走
while (pHead1.val != pHead2.val) {
pHead1 = pHead1.next;
pHead2 = pHead2.next;
}
//找到第一个相等的链表节点
return pHead1;
}
public static int getLength(Nodes pHead) {
int length = 0;
Nodes current = pHead;
while (current != null) {
length++;
current = current.next;
}
return length;
}
public static void main(String[] args) {
//测试用例
Nodes nodes6 = new Nodes(7, null);
Nodes nodes5 = new Nodes(6, nodes6);
Nodes nodes4 = new Nodes(4, nodes5);
Nodes nodes3 = new Nodes(3, nodes4);
Nodes nodes2 = new Nodes(2, nodes3);
Nodes nodes1 = new Nodes(1, nodes2);
Nodes nodes9 = new Nodes(7, null);
Nodes nodes8 = new Nodes(6, nodes9);
Nodes nodes7 = new Nodes(4, nodes8);
// nodes1.print();
// reversePrint(nodes1);
// Nodes nodes = deleteK(nodes1, 2);
// nodes1.print();
// System.out.println();
// Nodes nodes = deleteK(nodes1, 2);
// //输出倒数第二个节点的数值
// System.out.println(nodes.val);
// Nodes nodes = mergeNodes(nodes1, nodes7);
// nodes.print();
Nodes nodes = findFirstSame2(nodes1, nodes7);
System.out.println(nodes.val);
}
}
package offer;
import java.util.HashSet;
/*
链表环的入口节点
首先解决判断链表是否是个环
边界条件为一个节点或者 只有一个节点都不是环形链表
找到环入口的第一个节点
哈希表
快慢指针
*/
class RoundRoundNodes {
int val;
RoundRoundNodes next;
public RoundRoundNodes(int val) {
this.val = val;
}
public RoundRoundNodes(int val, RoundRoundNodes next) {
this.val = val;
this.next = next;
}
}
public class Test42 {
/**
* 判断链表是否有环
* 方法一 快慢指针
* 一个每次走两步 走两步走到null 么有环
* 一个每次都一步 走到相遇 有环
*
* @param head
* @return
*/
public static boolean containRound(RoundRoundNodes head) {
//判空
if (head == null || head.next == null) return false;
/*
//定义两个指针
RoundRoundNodes fast = head.next.next;
RoundRoundNodes slow = head;
while (fast != slow) {
fast = fast.next.next;
slow = slow.next;
if (fast == null) {
return false;
}
}
return true;
*/
//修改更简洁的算法
RoundRoundNodes fast = head;
RoundRoundNodes slow = head;
//判断条件
//保证不是最后一个的情况下
while (fast != null && fast.next != null) {
fast = fast.next.next;
slow = slow.next;
//两个指针相遇
if (fast == slow)
return true;
}
return false;
}
/**
* 方法二 用set记录数值 没往后走一个判断一个set里面是否有当前节点
*
* @param head
* @return
*/
public static boolean containRound2(RoundRoundNodes head) {
//判空
if (head == null || head.next == null) return false;
HashSet<RoundRoundNodes> set = new HashSet<>();
RoundRoundNodes cur = head;
while (cur != null) {
if (!set.contains(cur)) {
set.add(cur);
cur = cur.next;
} else {
return true;
}
}
return false;
}
/**
* 哈希表实现找到 环入口
* 报错不知道原因
*
* @param head
* @return
*/
public static RoundRoundNodes getFisrt(RoundRoundNodes head) {
if (head == null) return null;
//哈希表每次存储 放不进去就返回
RoundRoundNodes cur = head;
HashSet<RoundRoundNodes> set = new HashSet<>();
while (cur != null) {
//不要看包含 要看是否添加成功
//set.contains(cur) 不对
boolean flag = set.add(cur);
if (!flag) {
return cur;
}
cur = cur.next;
}
return null;
}
/**
* 双指针的方法 找到环第一个入口
* 先第一次返回一个节点 算整个环的长度 两个指针走相差节点的数值
*
* @param head
* @return
*/
public static RoundRoundNodes getFisrt2(RoundRoundNodes head) {
//为空或者为一个节点的情况
if (head == null || head.next == null) return null;
//找到一个环节点 构建两个指针
RoundRoundNodes fast = head;
RoundRoundNodes slow = head;
//判断条件 随便找到一个 在中间 不是第一个
//保证不是最后一个的情况下
while (fast != null && fast.next != null) {
fast = fast.next.next;
slow = slow.next;
//两个指针相遇
if (fast == slow) {
while (slow != head) {
slow = slow.next;
head = head.next;
}
return head;
}
}
return null;
}
public static void main(String[] args) {
//测试用例
RoundRoundNodes RoundNodes5 = new RoundRoundNodes(5);
RoundRoundNodes RoundNodes4 = new RoundRoundNodes(4, RoundNodes5);
RoundRoundNodes RoundNodes3 = new RoundRoundNodes(3, RoundNodes4);
RoundRoundNodes RoundNodes2 = new RoundRoundNodes(2, RoundNodes3);
RoundRoundNodes RoundNodes1 = new RoundRoundNodes(1, RoundNodes2);
RoundNodes5.next = RoundNodes3;
// System.out.println(containRound2(RoundNodes1));
RoundRoundNodes nodes = getFisrt2(RoundNodes1);
System.out.println(nodes.val);
}
}
java offer里面八道链表写了一天。。。没谁了