1、
题目:给定两个可能有环也可能无环的单链表,头节点head1,head2,请实现一个函数,若2个链表相交,请返回相交的第一个节点,若不相交,返回null
package learn;
public class Test {
public static class Node{
public int value;
public Node next;
public Node(int value){//构造方法
this.value=value;
}
}
public static Node getIntersectionNode(Node head1,Node head2) {//找两链表相交的第一个节点
if(head1==null || head2==null){
return null;
}
Node loop1=getLoopNode(head1);
Node loop2=getLoopNode(head2);
if(loop1==null && loop1==null){//两者都无环 从相交节点到尾一定都共有
return noLoop(head1,head2);
}else if(loop1!=null &&loop2!=null){
return bothLoop(head1,head2,loop1,loop2);
}else {
return null;
}
}
public static Node getLoopNode(Node head) {
if(head.next==null || head.next.next==null){//别忘了考虑边界情况 只有一个节点或者两个节点 不可能构成环
return null;
}
Node s=head.next;
Node f=head.next.next;
while (s!=f){//有环一定会相遇
if(f.next==null || f.next.next==null){//别忘了考虑无环情况
return null;//无环一定能走到尾且快指针先到尾 有环是无法走到尾的
}
s=s.next;
f=f.next.next;
}
f=head;
while (s!=f){
f=f.next;
s=s.next;
}
return s;
}
public static Node noLoop(Node head1,Node head2) {//都为无环链表
Node n1=head1;
Node n2=head2;
int len1=0,len2=0;
while (n1.next!=null){//n1指向表一末尾时退出
len1++;
n1=n1.next;
}
while (n2.next!=null){//n1指向表一末尾时退出
len2++;
n2=n2.next;
}
if(n1!=n2){
return null;
}
if(len1>len2){//n1指向长链表 n2指向短链表
n1=head1;
n2=head2;
}else {
n1=head2;
n2=head1;
}
int n=Math.abs(len1-len2);
while (n!=0){
n--;
n1=n1.next;
}
while (n1!=n2){
n1=n1.next;
n2=n2.next;
}
return n2;
}
public static Node bothLoop(Node head1,Node head2,Node loop1,Node loop2) {
Node n1=head1,n2=head2;
int n=0;
if(loop1==loop2){//将入环节点看为终止节点 将问题转化为无环节点交点问题
while (n1!=loop1){
n++;
n1=n1.next;
}
while (n2!=loop2){
n--;
n2=n2.next;
}
n1=n>0?head1:head2;
n2=(n1==head1)?head2:head1;
n=Math.abs(n);
while (n!=0){
n--;
n1=n1.next;
}
while (n1!=n2){
n1=n1.next;
n2=n2.next;
}
return n1;
}else {
n1=loop1.next;
while (n1!=loop1){
if(n1==loop2){
return loop1;
}
n1=n1.next;
}
return null;
}
}
}
2、二叉树遍历:
(1)递归:
package learn;
public class Test {
public static class Node{
public int value;
public Node left;
public Node right;
public Node(int value){//构造方法
this.value=value;
}
}
public static void preOrderTraverse(Node head){//根左右
if(head==null){
return;
}else {
System.out.println(head.value);
preOrderTraverse(head.left);
preOrderTraverse(head.right);
}
}
public static void inOrderTraverse(Node head){//左根右
if(head==null){
return;
}else {
preOrderTraverse(head.left);
System.out.println(head.value);
preOrderTraverse(head.right);
}
}
public static void posOrderTraverse(Node head){//左右根
if(head==null){
return;
}else {
preOrderTraverse(head.left);
preOrderTraverse(head.right);
System.out.println(head.value);
}
}
}
(2)非递归:
package learn;
import java.util.Stack;
public class Test {
public static class Node{
public int value;
public Node left;
public Node right;
public Node(int value){//构造方法
this.value=value;
}
}
public static void preOrderTraverse(Node head){//根左右
if(head==null){
return;
}else {
Stack<Node> s=new Stack<Node>();
s.push(head);
if(!s.empty()){
head=s.pop();
System.out.print(head.value+" ");//根
if(head.right!=null){//入栈:右左 出栈:左右
s.push(head.right);
}
if(head.left!=null){
s.push(head.left);
}
}
}
}
public static void inOrderTraverse(Node head){//左根右
if(head==null){
return;
}else {
Stack<Node> s=new Stack<Node>();
while (!s.empty() || head!=null){//head 每棵子树的根
if(head!=null){
s.push(head);
head=head.left;
}else{
head=s.pop();
System.out.print(head.value+" ");
head=head.right;
}
}
}
}
public static void posOrderTraverse(Node head){//左右根
if(head==null){
return;
}else {
Stack<Node> s1=new Stack<Node>();
Stack<Node> s2=new Stack<Node>();
s1.push(head);
while (!s1.empty()){
head=s1.pop();
s2.push(head);
if(head.left!=null){
s1.push(head.left);
}
if(head.right!=null){
s1.push(head.right);
}
}
while (!s2.empty()){
head=s2.pop();
System.out.println(head.value+" ");
}
}
}
}
二叉树的深度优先遍历即它的先序遍历
宽度优先遍历:队列
public static void widthTraverse(Node head){
if(head==null){
return;
}else {
Queue<Node> q=new LinkedList<Node>();//双向链表实现队列
q.add(head);
while (!q.isEmpty()) {
head = q.poll();
System.out.println(head.value + " ");
if (head.left != null) {
q.add(head.left);
}
if (head.right != null) {
q.add(head.right);
}
}
}
}
3、
(1)求二叉树中所有层中节点数的最大值(利用宽度优先遍历)
package learn;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Queue;
public class Test {
public static class Node{
public int value;
public Node left;
public Node right;
public Node(int value){//构造方法
this.value=value;
}
}
public static int widthTraverse(Node head) {
if (head == null) {
return 0;
} else {
Queue<Node> q = new LinkedList<Node>();//双向链表实现队列
HashMap<Node, Integer> h = new HashMap<Node, Integer>();
int level = 1;//当前层
int nodeLevel = 1;//节点所在层
int levelNodesNum = 0;//当前层节点数统计
int max = Integer.MIN_VALUE;
h.put(head, nodeLevel);
q.add(head);
while (!q.isEmpty()) {
head = q.poll();
if (h.get(head) == level) {
levelNodesNum++;
} else {//现在观测的节点不在当前统计的层了
max = Math.max(max, levelNodesNum);//结算当前节点的上一层节点数和最大值谁大谁小
level++;//结算后进入下一层(即当前节点所在层)
levelNodesNum = 1;//当前节点为此层记录的第一个节点
}
if (head.left != null) {
q.add(head.left);
h.put(head.left, level + 1);//head的子女在它的下一层
}
if (head.right != null) {
q.add(head.right);
h.put(head.right, level + 1);
}
}
max = Math.max(max, levelNodesNum);
return max;
}
}
}
(2)二叉树最大宽度(leetcode 662)
同样利用宽度优先遍历,与求所有层最大节点个数不同的是,比如第三层的节点为[v1,v2,null,v4]时,最多节点个数是3,而最大宽度是4。可以使用双端队列的方式,最右节点下标-最左节点下标+1 即为当前宽度。
两个hashmap 一个存放节点对应的下标 一个存放节点对应的层。
每次到下一层的时候,结算上一层。上一层刚结束时,队列中节点是只有当前层的,当前层的下一层节点还未入队。
package learn;
import java.util.Deque;
import java.util.HashMap;
import java.util.LinkedList;
public class Test {
public static class Node {
public int value;
public Node left;
public Node right;
public Node(int value) {//构造方法
this.value = value;
}
}
public static int widthTraverse(Node head) {
if (head == null) {
return 0;
} else {
Deque<Node> dq = new LinkedList<Node>();//双端队列
HashMap<Node, Integer> h1 = new HashMap<Node, Integer>();//存放当前节点的下标
HashMap<Node, Integer> h2 = new HashMap<Node, Integer>();//存放当前节点所在层
dq.add(head);
int nowNodeLevel=1;
int nowMax=1;//head!=null 说明至少一个节点 宽度至少为1
h1.put(head,0);
h2.put(head,nowNodeLevel);
while (!dq.isEmpty()){
Node cur=dq.peek();
int i=h1.get(cur);//当前节点的下标
if(h2.get(cur)!=nowNodeLevel){//上一层刚结束时,队列中节点是只有当前层的,当前层的下一层节点还未入队
nowMax=Math.max(h1.get(dq.getLast())-h1.get(dq.getFirst())+1,nowMax);
nowNodeLevel++;//进入当前层
}
dq.poll();
if(cur.left!=null){
h1.put(cur.left,2*i+1);
h2.put(cur.left,nowNodeLevel+1);
dq.add(cur.left);
}
if(cur.right!=null){
h1.put(cur.right,2*i+2);
h2.put(cur.right,nowNodeLevel+1);
dq.add(cur.right);
}
}
return nowMax;
}
}
}