持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第8天,点击查看活动详情
最近开始接触与学习数据结构与算法,一方面为了解决课内数据结构课解题思路不清晰的问题,另一方面也是听说左神的大名,故开始跟着左神一起学习数据结构与算法。同时以写博客的形式作为输出,也算是为了对所学的知识能掌握的更深吧
两个单链表相交的一系列问题
题目:给定两个可能有环也可能无环的单链表,头节点head1和head2.请实现一个函数,如果两个链表相交,返回相交的第一个节点。如果不相交,返回null 要求:如果i两个链表长度之和为N,时间复杂度请达到O(N),额外空间复杂度请达到O(1)
一条链表有环的情况找出环的第一个节点: 思路:快慢指针,快指针一次走两步,慢指针一次走一步,直到两个指针第一次相遇,然后慢指针原地不动,快指针回到原点,两个指针一次走一步,当快慢指针再一次相遇时,他们相遇的位置一定是环的起点位置。【数学证明过程略】
代码实现:
public static Node getLoopNode(Node head){
if(head == null || head.next == null || head.next.next == null)
return null;
Node fastNode = head;
Node slowNode = head;
while(fastNode != slowNode){
if(fastNode.next==null || fastNode.next == null) return null;
fastNode = fastNode.next.next;
slowNode = slowNode.next;
}
fastNode = head;
while(fastNode !=slowNode){
fastNode = fastNode.next;
slonNode = slowNode.next;
}
return slowNode;
}
两个链表都无环的情况,找出相交的第一个节点 思路:由于单链表一个节点只有一个next,所以不存在两条单链表相交后分离的情况,所以首先判断两条链表的最后一个节点是否是同一个。如果是的话就说明两条单链表存在公共部分,然后判断两条单链表长度差值x,让长的那条先走x距离,然后两条链表同时遍历,当两个指针相等时所在的位置就是第一个节点
代码实现: ^7b27dd
public static Node noLoop(Node head1,Node head2){
if(head1 == null || head2 == null) return null;
Node cur1 = head1;
Node cur2 = head2;
int n = 0;
while(cur1.next!=null){
n++;
cur1 = cur1.next;
}
while(cur2.next!=null){
n++;
cur2 = cur2.next;
}
if(cur1!=cur2){
//比较结尾
return null;
}
cur1 = n>0 ? head1:head2;
cur2 = cur1 == head1 ? head2 : head1;
n = Math.abs(n);
while(n!=0){
n--;
cur1 = cur.next;
}
while(cur1!=cur2){
cur1 = cur1.next;
cur2 = cur2.next;
}
return cur1;
}
两个链表都有环的情况,返回第一个相交的节点 存在三种情况:
思路:可以根据两条链表的环部分的第一个节点loop1和loop2是否相等分成两种,第一种是2的情况,loop1 == loop2;第二种是1和3的情况,loop1 != loop2。
- 2情况可以将loop1以前的部分看成两个链表都无环的情况,直接找出相交的第一个节点即可
- 1和3的区分思路是:让一个指针从loop1出发,如果在它回到原来的位置前遇到了loop2,就是情况3,否则就是情况1
代码实现:
public static Node bothLoop(Node head1,Node loop1,Node head2,Node loop2){//loop1和loop2可以通过上面的getLoopNode函数得到
Node cur1 = null;
Node cur2 = null;
if(loop1 == loop2){
//情况2
int n = 0;
cur1 = head1;
cur2 = head2;
while(cur1.next!=null){
n++;
cur1 = cur1.next;
}
while(cur2.next!=null){
n--;
cur2 = cur2.next;
}
cur1 = n > 0 ? head1 : head2;//cur1谁长就是谁
cur2 = cur1 == head1 : head2 : head1;
n = Math.abs(n);
while(n!=0){
n--;
cur1 = cur1.next;
}
while(cur1!=cur2){
cur1 = cur1.next;
cur2 = cur2.next;
}
}else{
cur1 = loop1;
while(cur1!=loop1){
if(cur1 == loop2){
return loop1;
}
cur1 = cur1.next;
}
return null;
}
}
遍历二叉树的三种的方式
二叉树的节点结构
Class Node<V>{
V value;
Node left;
Node right;
}
- 先序(头左右):对于二叉树中的每一个子树来说,都是先遍历头节点,然后遍历左节点再遍历右节点
- 中序(左头右):对于二叉树中的每一个子树来说,都是先遍历左节点,然后遍历头节点再遍历右节点
- 后序(左右头):对于二叉树中的每一个子树来说,都是先遍历左节点,然后遍历右节点再遍历头节点
代码实现:
public static void method(Node head){
if(head == null) return;
//在此处对head进行操作就是先序
method(head.left);
//在此处对head进行操作就是中序
method(head.right);
//在此处对head进行操作就是后序
}
一般使用递归进行三种顺序的遍历二叉树
栈与二叉树的混合运用——使用迭代的方式遍历二叉树
使用栈先序遍历二叉树
代码实现:
public static void preOrderUnRecur(Node head){
if(head != null){
Stack<Node> stack = new Stack<Node>();
stack.add(head);
while(!stack.isEmpty()){
head = stack.pop();//弹出元素
/*
这里可以对head进行处理,比如打印等操作
*/
if(head.right!=null){
stack.push(head.right);//压入右节点
}
if(head.left!=null){
stack.push(head.left);//压入左节点
}
}
}
使用栈后序遍历二叉树:
思路:利用栈先进后出的特性,每次在对弹出栈的元素进行处理时将该元素压入新的一个收集栈中,这样子在遍历收集栈中元素时就是先序的逆序(后序)
代码实现不难,可以自行实现
使用栈中序遍历二叉树:
思路:将每棵树的左节点全部压入栈中,然后开始将元素弹出栈,每弹出一个元素cur,就进行相应操作,然后看cur有没有右子树,如果有的话,就把右子树下的所有左节点压入栈中,再继续弹出元素。重复此操作
代码实现:
public static void inOrderUnRecur(Node head){
if(head!=null){
Stack<Node> stack = new Stack<Node>;
while(!stack.isEmpty() || head != null){
if(head.left!=null){
//有左节点
stack.push(head.left);
head = head.left;
}else{
head = stack.pop();
/*
进行处理,比如打印等操作
*/
head = head.right;
}
}
}
}
二叉树的宽度优先遍历
求一棵二叉树的宽度——使用队列
思路图解:
代码实现:
public static void sort(Node head){
if(head==null) return;
//创建一个队列
Queue<Node> queue = new LinkedList<>();
queue.add(head);
while(!queue.isEmpty()){
Node cur = queue.poll();//弹出元素
/*
进行操作,比如打印
*/
if(cur.left!=null) queue.add(cur.left);
if(cur.right!=null) queue.add(cur.right);
}
}
拓展:找出二叉树中宽度最大的一层有多少个节点
思路: 使用curLevel记录当前遍历所在的层数,使用curLevelNodes记录下当前层数有多少个节点,使用max记录目前为止宽度最大的一层的节点数 使用哈希表,在每次队列进入新的节点时记录下该结点所在的层数,每次队列弹出元素时判断该元素是不是属于当前层curLevel,是的话curLevelNodes++,不是的话更新max,然后记录新一层的节点数量
代码实现:
public static int getMaxNodes(Node head){
if(head == null) return n0;
Queue<Node> queue = new LinkedList<>();
queue.add(head);//根节点进入队列
HashMap<Node,Integer> levelMap = new HashMap<>();//记录节点与层数的对应关系
levelMap.put(head,1);
int curLevel = 1;
int curLevelNodes = 0;
int max = Integer.MIN_VALUE;
while(!queue.isEmpty()){
//弹出队列中的元素
cur = queue.poll();
int curNodeLevel = levelMap.get(cur);//当前节点所在的层数curNodeLevel
//判断该元素与当前层数的关系,是的话curLevelNodes++,不是的话curLevel+1且curLevelNodes重置为1;更新max是否是上一层的节点数
if(curNodeLevel==curLevel){
curLevelNodes++;
}else{
max = Math.max(curLevelNodes,max);
curLeve++;
curLevelNodes = 1;
}
//队列加入弹出节点的左节点和右节点,同时记录下加入的节点与它所在层数的关系
if(cur.left!=null){
queue.add(cur.left);
levelMap.put(cur.left,curNodeLevel+1);
}
if(cur.right!=null){
queue.add(cur.right);
levelMap.put(cur.right,curNodeLevel+1);
}
}
}