链表结构题
链表结构面试题
以下题目通用的链表结构类
package Link;
public class LinkNode {
public int value;
public LinkNode next;
public LinkNode rand;
public boolean isCopy;
public LinkNode(){
}
public LinkNode(int value){
this.value = value;
this.next = null;
this.isCopy = false;
}
public LinkNode(LinkNode node){
this.next = node.next;
this.value = node.value;
this.rand = node.rand;
this.isCopy = true;
}
public void addNode(LinkNode head, int value){
LinkNode node = head;
while(node != null) node = node.next;
LinkNode tmp = new LinkNode(value);
node.next = tmp;
}
public LinkNode createTree(int[] arr){
LinkNode head = new LinkNode(arr[0]);
LinkNode tmp = head;
for(int i=1; i<arr.length; i++){
LinkNode node = new LinkNode(arr[i]);
tmp.next = node;
tmp = tmp.next;
}
return head;
}
}
题目一: 判断链表是否为回文结构
package Link;
public class LinkMain {
public static void main(String[] args){
//生成链表
int[] arr = new int[]{1,2,3,4,3,2,1};
LinkNode node = new LinkNode();
LinkNode head = node.createTree(arr);
LinkPractice linkPractice = new LinkPractice();
/**
* 题目一:判断单链表是否是回文结构
* */
//解法一:不考虑空间复杂度和时间复杂度
boolean result = linkPractice.isPallindrome1(head);
System.out.println("是否是回文:"+result);
//解法二:反转链表,考虑空间复杂度
boolean result2 = linkPractice.isPalindrome3(head);
System.out.println("是否是回文:"+result2);
}
package Link;
import javax.swing.tree.TreeNode;
import java.util.HashMap;
import java.util.Stack;
public class LinkPractice {
/**
* 判断回文
*/
//解法一:使用栈
public boolean isPallindrome1(LinkNode head){
LinkNode node = head;
Stack<LinkNode> stack = new Stack<>();
while(node != null) {
stack.push(node);
node = node.next;
}
node = head;
while (node!= null){
if(node.value != stack.pop().value) return false;
node = node.next;
}
return true;
}
//方法二:使用反转链表方法
public boolean isPalindrome3(LinkNode head){
if(head == null || head.next == null) return true;
LinkNode node1 = getMidNode(head); //node1为中点
LinkNode node2 = node1.next;
LinkNode node3 = null; //辅助结点
node1.next = null;
while (node2 != null){
node3 = node2.next; //保存node2下一个结点
node2.next = node1; //将node2的下一个结点指向node1
node1 = node2; //将node2给到node1,以进行下一次反转指向
node2 = node3; //将node3给到node2,以进行下一次反转指向
}
node3 = node1;
node2 = head;
boolean res = true;
while(node1 != null || node2 != null){
if(node1.value != node2.value) {
res = false;
break; //后面要将链表还原,不可以直接返回
}
node1 = node1.next;
node2 = node2.next;
}
//还原链表
node1 = node3.next;
node3.next = null;
while (node1!=null){
node2 = node1.next;
node1.next = node3;
node3 = node1;
node1 = node2;
}
return res;
}
//快慢指针找中间位置 --链表
public LinkNode getMidNode(LinkNode head){
LinkNode node1 = head;
LinkNode node2 = head;
while(node2.next != null && node2.next.next != null){
node1 = node1.next;
node2 = node2.next.next;
}
return node1;
}
}
题目二:将链表分为大于区,小于去和等于区
package Link;
public class LinkMain {
public static void main(String[] args){
//生成链表
int[] arr = new int[]{1,2,4,3,2,1,3};
LinkNode node = new LinkNode();
LinkNode head = node.createTree(arr);
LinkPractice linkPractice = new LinkPractice();
/**
* 题目二: 将一个链表分为大于区,小于区和等于区
* */
//解法一:使用数组partation
head = linkPractice.listPartation1(head);
printTreeNodeValue(head);
//解法二:使用六个变量,保证无额外空间的使用
head = linkPractice.listPartation2(head);
printTreeNodeValue(head);
}
package Link;
import javax.swing.tree.TreeNode;
import java.util.HashMap;
import java.util.Stack;
public class LinkPractice {
//解法一:使用数组做partation
public LinkNode listPartation1(LinkNode head) {
if (head == null || head.next == null) return head;
LinkNode tmp = head;
int length = 0,index=0;
while(tmp != null){
length++;
tmp = tmp.next;
}
tmp = head;
LinkNode[] arr = new LinkNode[length];
while(tmp != null){
arr[index] = tmp;
tmp = tmp.next;
arr[index].next = null;
index++;
}
arr = Arraypartation(arr,0,length-1);
head = arr[0];
tmp = head;
for(int i=1; i<arr.length; i++){
tmp.next = arr[i];
tmp = tmp.next;
}
return head;
}
//将数组做partatin
public LinkNode[] Arraypartation(LinkNode[] arr, int L, int R){
int less = L-1;
int more = R;
while(L<more){
if(arr[L].value < arr[R].value) swap(arr,++less,L++);
else if(arr[L].value > arr[R].value) swap(arr,--more,L);
else L++;
}
swap(arr,more,R);
return arr;
}
//数组元素交换
public void swap(LinkNode[] arr, int index1, int index2){
LinkNode node = arr[index1];
arr[index1] = arr[index2];
arr[index2] = node;
}
//解法二:使用六个变量,无须额外的空间
public LinkNode listPartation2(LinkNode head){
if(head == null || head.next == null) return head;
LinkNode sh=null,st=null,eh=null,et=null,mh=null,mt=null,comparison=head,tmp=null;
while (comparison.next!=null) comparison=comparison.next;
while (head != null) {
tmp = head.next;
head.next = null;
if (head.value < comparison.value) { //小于区域
if (sh == null) {
sh = head;
st = head;
} else {
st.next = head;
st = head;
}
} else if (head.value == comparison.value) { //等于区域
if (eh == null) {
eh = head;
et = head;
} else {
et.next = head;
et = head;
}
} else {
if (mh == null) {
mh = head;
mt = head;
} else {
mt.next = head;
mt = head;
}
}
head = tmp;
}
if(st!=null){
st.next = eh;
et = et==null?st:et; //如果没有等于区域,则事小区域去链接,如果有,则是等于区域去连街
}
if(et!=null){
et.next = mh;
}
return sh!=null?sh:(eh!=null?eh:mh);
}
}
题目三: 链表节点中,有一个rand指针,指向任意结点,复制该链表
package Link;
public class LinkMain {
public static void main(String[] args){
//创建链表
LinkNode tmp = head.next;
tmp.rand = tmp.next.next;
tmp = tmp.next;
tmp.rand = tmp.next.next;
tmp = tmp.next;
tmp.rand = head;
//解法一: 使用map
LinkNode copyNode = linkPractice.mapCopyLink(head);
System.out.println("原链表");
printTreeNode(head);
System.out.println("复制链表");
printTreeNode(copyNode);
//解法二: 在原链表上进行复制
LinkNode copyNode = linkPractice.copyLink(head);
System.out.println("原链表");
printTreeNode(head);
System.out.println("复制链表");
printTreeNode(copyNode);
}
package Link;
import javax.swing.tree.TreeNode;
import java.util.HashMap;
import java.util.Stack;
public class LinkPractice {
//解法一: 使用map
public LinkNode mapCopyLink(LinkNode head){
if(head == null || head.next == null) return head;
HashMap<LinkNode,LinkNode> linkMap = new HashMap<>();
LinkNode tmp = head;
while (tmp!=null){
linkMap.put(tmp,new LinkNode(tmp));
tmp = tmp.next;
}
tmp = head;
while(tmp !=null){
linkMap.get(tmp).next = linkMap.get(tmp.next);
linkMap.get(tmp).rand = linkMap.get(tmp.rand);
tmp = tmp.next;
}
return linkMap.get(head);
}
//解法二: 在原链表里面复制进行操作
public LinkNode copyLink(LinkNode head){
if(head == null || head.next == null) return head;
LinkNode tmp = head,node = null,copyNode = null;
//在自身复制出每个结点成为自己的下一个结点
while (tmp !=null){
node = tmp.next;
tmp.next = null;
copyNode = new LinkNode(tmp.value);
copyNode.isCopy = true;
tmp.next = copyNode;
tmp.next.next = node;
tmp = node;
}
//调整复制链表的rand属性
tmp = head;
node = null;
copyNode = null;
while (tmp!=null){
copyNode = tmp.next;
node = tmp.next.next;
copyNode.rand = tmp.rand != null ? tmp.rand.next : null;
tmp=node;
}
//将原链表和复制链表拆分
LinkNode copyNodeHead = head.next;
tmp = head;
node = null;
copyNode = null;
while (tmp != null){
node = tmp.next.next;
copyNode = tmp.next;
copyNode.next = node != null ? node.next:null;
tmp = node;
}
return copyNodeHead;
}
}
题目四:一个链表上面从某一个结点开始进入循环,求入环结点
package Link;
public class LinkMain {
public static void main(String[] args){
//创建链表
LinkNode tmp = head,interLoop = null;
interLoop = tmp.next.next;
while (tmp.next!=null) tmp = tmp.next;
tmp.next = interLoop;
//解法一: 使用集合
LinkNode result = linkPractice.getInterLoopNodeByMap(head);
System.out.println("入环结点是:"+result.value);
//解法二: 使用快慢指针
LinkNode result = linkPractice.getInterLoopNdoe(head);
System.out.println("入环结点是:"+result.value);
}
package Link;
import javax.swing.tree.TreeNode;
import java.util.HashMap;
import java.util.Stack;
public class LinkPractice {
//解法一: 使用栈
public LinkNode getInterLoopNodeByMap(LinkNode head){
if(head == null || head.next == null) return head;
LinkNode tmp = head;
HashMap<LinkNode,LinkNode> nodeMap = new HashMap<>();
boolean flat=false;
while(flat == false){
if(nodeMap.containsKey(tmp)) {
flat = true;
}
else {
nodeMap.put(tmp,tmp);
tmp =tmp.next;
}
}
return tmp;
}
//解法二: 使用快慢指针
public LinkNode getInterLoopNdoe(LinkNode head){
if(head == null || head.next == null) return null;
LinkNode slow = head.next;
LinkNode quick = head.next.next;
while (slow != quick){
if(quick.next == null || slow == null) return null;
slow = slow.next;
quick = quick.next.next;
}
quick = head;
while (quick != slow){
quick = quick.next;
slow = slow.next;
}
return quick;
}
}
题目五: 有俩条不确定是否有环的链表,请判断俩条链表是否相交,如果相交,返回相交的第一结点
package Link;
public class LinkMain {
public static void main(String[] args){
//创建链表
LinkNode tmp1 = head,interLoop = null;
interLoop = tmp1.next.next;
while (tmp1.next!=null) tmp1 = tmp1.next;
tmp1.next = interLoop;
LinkNode tmp2 = new LinkNode(3);
tmp2.next = new LinkNode(4);
tmp2.next.next = interLoop.next;
LinkNode linkNode = linkPractice.getIntersectNode(head,tmp2);
System.out.println("相交结点是:"+linkNode.value);
}
package Link;
import javax.swing.tree.TreeNode;
import java.util.HashMap;
import java.util.Stack;
public class LinkPractice {
/**
* 判断链表是否相交
* */
public LinkNode getIntersectNode(LinkNode head1,LinkNode head2){
if(head1 == null || head2 == null || head1.next == null || head2.next == null)return null;
LinkNode linkNode = null;
//获取入环结点
LinkNode interLoop1 = getInterLoopNdoe(head1);
LinkNode interLoop2 = getInterLoopNdoe(head2);
if(interLoop1 == null && interLoop2 == null){
linkNode = noLoop(head1,head2);
}
if(interLoop1 != null && interLoop2 != null){
linkNode = bothLoop(head1,interLoop1,head2,interLoop2);
}
return linkNode;
}
//获取入环结点
public LinkNode getInterLoopNdoe(LinkNode head){
if(head == null || head.next == null) return null;
LinkNode slow = head.next;
LinkNode quick = head.next.next;
while (slow != quick){
if(quick.next == null || slow == null) return null;
slow = slow.next;
quick = quick.next.next;
}
quick = head;
while (quick != slow){
quick = quick.next;
slow = slow.next;
}
return quick;
}
//当俩条链表均没有环时判断是否相交
public LinkNode noLoop(LinkNode head1,LinkNode head2){
if(head1 == null || head2 == null || head1.next == null || head2.next == null)return null;
//判断是否相交
LinkNode tmp1 = head1;
LinkNode tmp2 = head2;
int difference = 0;
while (tmp1 != null) {
tmp1 = tmp1.next;
difference++;
}
while (tmp2 != null) {
tmp2 = tmp2.next;
difference--;
}
if(tmp1 != tmp2) return null; //不相交
else {
LinkNode cur = difference > 0 ? tmp1 : tmp2;
LinkNode tmp = cur == tmp1 ? tmp2 : tmp1;
difference = Math.abs(difference);
for (int i=0; i<difference; i++) cur = cur.next; //先走完长度差,不会在这段距离内相交
while (cur != tmp){
cur = cur.next;
tmp = tmp.next;
}
return cur;
}
}
//当俩个链表都有环时
public LinkNode bothLoop(LinkNode head1,LinkNode interNode1,LinkNode head2,LinkNode interNode2){
if(head1 == null || head2 == null || head1.next == null || head2.next == null)return null;
if (interNode1 == interNode2){ //入环前相交
//判断是否相交
LinkNode tmp1 = head1;
LinkNode tmp2 = head2;
int difference = 0;
while (tmp1 != interNode1) {
tmp1 = tmp1.next;
difference++;
}
while (tmp2 != interNode2) {
tmp2 = tmp2.next;
difference--;
}
if(tmp1 != tmp2) return null; //不相交
else {
LinkNode cur = difference > 0 ? head1 : head2;
LinkNode tmp = cur == head1 ? head2 : head1;
difference = Math.abs(difference);
for (int i=0; i<difference; i++) cur = cur.next; //先走完长度差,不会在这段距离内相交
while (cur != tmp){
cur = cur.next;
tmp = tmp.next;
}
return cur;
}
}else { //相交 则任意一个入环结点都可以
LinkNode cur = interNode1.next;
while (cur != interNode1){
if (cur == interNode2) return cur;
cur = cur.next;
}
return null; //不相交
}
}
}