题目三 判断一个链表是否为回文
笔试思路:放入栈中,弹出的顺序为逆序地顺序,然后与原链表依次比较不同则不为回文。利用快慢指针来判断这个链表中点然后只放入一半进入栈中。
public static class Node {
public int data;
public Node next;
public Node(int data){this.value = data;}
}
//开辟n的额外数据空间
public static boolean isPalindrome1(Node head){
Stack<Node> stack = new Stack<>();
Node cur = head;
while(cur != null){
stack.push(cur);
cur = cur.next;
}
boolean res = true;
while(!stack.isEmpty()){
if(head.value != stack.pop().value){
res = false;
}
head = head.next;
}
return res;
}
//运用快慢指针开辟n/2的额外空间
public static boolean isPalindrome2(Node head){
Node cur = head;
Node right = head;
while(cur.next != null && right.next.next != null ){
cur = cur.next;
right = right.next.next;
}
cur = cur.next;
Stack<Node> stack = new Stack<>();
while(cur.next != null){
stack.push(cur);
cur = cur.next;
}
boolean res = true;
while(!stack.isEmpty()){
if(head.value != stack.pop().value){
res = false;
}
head = head.next;
}
return res;
}
面试思路:用快慢指针找出中点,中点上的next断掉之后,后面的进行逆序,最后一个往前找的同时第一个往后找,看指向是否相同,不同则返回false,相同返回true。返回之前恢复链表。
public static boolean isPalindrome3(Node head){
if(head == null || head.next == null){
return true;
}
Node n1 = head;
Node n2 = head;
while(n1.next != null && n2.next.next != null){
n1 = n1.next; //到了mid;
n2 = n2.next.next;//到了end;
}
n2 = n1.next;//右边部分第一个
n1.next = null;//把中间节点断开
Node n3 =null;
while(n2!= null){//倒序后半部分
n3 = n2.next;
n2.next = n1;
n1 = n2;
n2 = n3;
}
//此时n1为最后的一个节点
n3 = n1;
n2 = head;
boolean res = true;
while(n2 != null ){
if(n1 != n2){
res =false;
break;
}
n2 = n2.next;
n1 = n1.next;
}
n1 = n3.next;
n3.next = null;
while(n1 != null){
n2 = n1.next;
n1.next = n3;
n3 = n1;
n1 = n2;
}
return res;
}
题目四 将单向链表按某值划分成左边小、中间相等、右边大的形式,保持相对次序不变
笔试方法:将链表放入链表数组中,运用数组的partition方法,将链表划分。
public static class Node{
public int value;
public Node next;
public Node(int data){this.value = data;}
}
public static Node listPartition1(Node head,int pivot){
Node cur = head;
int i = 0;
while(cur != null){
i ++;
cur = cur.next;
}
Node[] arrNode = new Node[i];
i = 0;
for(int i = 0; i < arrNode.length; i ++){
arrNode[i] = head;
head = head.next;
}
arrPartition(arrNode,0,arrNode.length-1,pivot);//重新划分链表数组
//将链表数组中的节点重新串成链表并返回
for(i = 1 ; i != arrNode.length; i ++){
arrNode[i-1].next = arrNode[i];
}
return arrNode[0];
}
public staic void arrPartition(Node[] arrNode,int l,int r,int pivot){
int less = l-1;
int more = r + 1;
int index = 0;
while(index != r){
if(arrNode[index].value < pivot){
swap(arrNode,index++,++less);
}else if(arrNode[inde].value > pivot){
swap(arrNode,index,--more);
}else{
index ++;
}
}
}
public static void swap(Node arrNode ,int i; int j){
Node tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
面试方法:首先将每个节点与原链表断开,运用六个变量,sH,sT,eH,eT,bH,bT。分别表示小于、等于、大于区域的头和尾节点,从第一个开始依次判断与划分值的关系,如果小于则先判断头和尾是否空为空则此值作为头和尾,当下一个小于值到来的时候,将其作为上一个尾的连接之后将其作为新的尾。等三个区域都划分好之后,头尾相连成为新的链表即可。
public static class Node{
public int value;
public Node next;
public Node(int data){this.value = data;}
}
public static Node listPartition2(Node head,int pivot){
Node sH = null;//设置六个变量
Node sT = null;
Node eH = null;
Node eT = null;
Node bH = null;
Node bT = null;
Node next = null;//记录下一个节点
if(head == null || head.next == null){
return head;
}
while(head != null){
next = head.next;//记录下一个节点
head.next = null;//head与原链表断开
if(head.value < pivot){
if(sH == null && sT == null){
sH = head;
sT = head;
}else{
sT.next = head;//区域的尾节点指向这个新的节点
sT = head;//这个新节点成为尾节点
}
}else if(head.value == pivot){
if(eH == null && eT == null){
eH = head;
eT = head;
}else{
eT.next = head;//区域的尾节点指向这个新的节点
eT = head;//这个新节点成为尾节点
}
}else{
if(bH == null && bT == null){
bH = head;
bT = head;
}else{
bT.next = head;//区域的尾节点指向这个新的节点
bT = head;//这个新节点成为尾节点
}
}
head = next;
}
if(sT != null){
sT.next = eH;
}
if(eT != null){
eT.next = bH;
}
return sH != null ? sH:eH != null ? eH : bH;
}
题目五 复制含有随机指针节点的链表
【题目】一种特殊的单链表节点类描述如下
class Node {
int value;
Node next;
Node rand;
Node(int val) {value = val;}
}
rand指针是单链表节点结构中新增的指针,rand可能指向链表中的任意一个节点,也可能指向null。给定一个由Node节点类型组成的无环单链表的头节点head,请实现一个函数完成这个链表的复制,并返回复制的新链表的头节点。
笔试思路:采用额外空间HashMap,将Key放入原来的链表Node,value中放入新的链表节点。然后遍历老链表,从HashMap中得到老链表next和rand指针的指向,使得新链表的rand和next也指向老链表的value中的对应的新链表节点,返回新链表的头即可。
public static Node copyList(Node head){
HashMap<Node,Node> hashMap1 = HahsMap<>();
Node cur = head;
while(cur != null){
hashMap1.put(cur,new Node(cur.value));
cur = cur.head;
}
cur = head;
while(cur! = null){
cur.rand = hashMap1.get(cur).rand;
cur.next = hashMap1.get(cur).next;
cur = cur.next;
}
return hashMap1.get(cur);
}
面试思路:不采用HashMap方法,生成克隆节点,将其连在老链表节点的后面,其中克隆节点的rand指针就是采用,原节点指向的节点的next就可以找到克隆节点的rand指针所指向的节点。
public static Node copyList2(Node head){
Node cur = head;
Node nextOne =null;
//将克隆节点复制值并放在原节点之后
while(cur != null){
next = cur.next;
cur.next = new Node(cur.value);//其后为新建的克隆节点
cur.next.next = nextOne;//克隆节点连接原节点的下一个
cur = nextOne;//继续往后
}
cur = head;
while(cur != null){
nextOne = cur.next.next;
Node copyNode = cur.next;
copyNode.rand = cur.rand != null ? cur.rand.next : null;//克隆节点的rand指向原节点rand指针指向节点的next
cur = nextOne;
}
cur = head;
Node res = head;
//拆开
while(cur != null){
nextOne = cur.next.next;
Node copyNode = cur.next;
cur.next = nextOne;
copyNode.next = nextOne != null ? nextOne.next : null;
cur = nextOne;
}
return res;
}
题目六 两个单链表相交的一系列问题
【题目】给定两个可能有环也可能无环的单链表,头节点head1和head2。请实现一个函数,如果两个链表相交,请返回相交的 第一个节点。如果不相交,返回null
【要求】如果两个链表长度之和为N,时间复杂度请达到O(N),额外空间复杂度请达到O(1)
首先要判断是否有环,写一个函数返回第一个入环的节点`
思路1:利用HashSet装入链表,遍历原链表的同时从HashSet中寻找,如果存在则有环,否则无环。
public static Node isCircle(Node head){
HashSet<Node> hashSet1 = new HashSet<>();
Node cur = head;
while(cur != null){
if(hashSet1.contains(cur)){
return cur;
}
hashSet1.add(cur);
cur = cur.next;
}
return null;
}
思路2:利用快慢指针,分别一次走两步和一步,其中一个到了null则无环,若两个相遇则有环。相遇的时候让快指针返回开头,两个指针同时走一步一次,他们再次相遇则就为入环的第一个节点(定理)
public static Node isCircle2(Node head){
Node n1 = head.next;
Node n2 = head.next.next;
while (n1 != n2){
if (n1.next == null || n2.next.next == null){
return null;
}
n1 = n1.next;
n2 = n2.next.next;
}
n2 = head;
while (n1 != n2){
n1 = n1.next;
n2 = n2.next;
}
return n1;
}
看两个无环单链表是否相交,首先统计两个的长度和结尾节点,首先判断结尾是否相同,不同则不相交,如相同则让长的先走差值步,之后再一起走,两者相等则是第一个相交的节点处
public static Node bothNoCircle(Node head1,Node head2){
Node cur1 = head1;
Node cur2 = head2;
int i = 0;//用i来计算两个链表的差值
while (cur1.next != null){//因为后面需要比较cur1和cur2是否相同,所以让next不为null
i ++;
cur1 = cur1.next;
}
while (cur2.next != null){
i --;
cur2 = cur2.next;
}
if (cur1 != cur2){//判断结尾是否一样,不同则不相交
return null;
}
//这时让长的为cur1,短的为cur2;
cur1 = i > 0 ? head1 : head2;
cur2 = cur1 == head1 ? head2 : head1;
i = Math.abs(i);
while (i != 0){
cur1 = cur1.next;
i --;
}
while (cur1 != cur2){
cur1 = cur1.next;
cur2= cur2.next;
}
return cur1;
}
一个有环一个无环不可能相交
两个链表都有环,有以下三种情况,首先第二种情况把入环点作为终止节点,运用之前两个无环链表相交的方法找出相交的第一个节点
如何区分一和三呢,采用先找到一个入环点,使其往下走,如果在遇到自己之前和另一个链表的入环点遇上则相交。
public static Node bothCircle(Node head1 , Node head2) {
Node loop1 = isCircle1(head1);
Node loop2 = isCircle1(head2);
Node cur1 = head1;
Node cur2 = head2;
if (loop1 == loop2) {
int i = 0;
while (cur1 != loop1) {
i++;
cur1 = cur1.next;
}
while (cur2 != loop2) {
i--;
cur2 = cur2.next;
}
cur1 = i > 0 ? head1 : head2;
cur2 = cur1 == head1 ? head2 : head1;
i = Math.abs(i);
while (i != 0) {
cur1 = cur1.next;
i--;
}
while (cur1 != cur2) {
cur1 = cur1.next;
cur2 = cur2.next;
}
return cur1;
}else {
cur1 = loop1.next;
while (cur1 != loop1){
if (cur1 == loop2){
return cur1;
}
cur1 = cur1.next;
}
return null;
}
}