面试与笔试的不同点:
打印两个链表的公共部分
题目:
思路:
分别取两个指针只想两个链表的头指针,比较大小,小的那个数指针后移,遇到两个值相同的情况,则打印这个值,并同时后移,直到有一个链表为空,则结束移动;
coding:
判断一个链表是否为回文结构
题目:
结点定义如下:
public static class Node {
public int value;
public Node next;
public Node(int data) {
this.value = data;
}
}
思路一:
我开辟一个栈的空间,从头结点开始遍历链表,依次将得到的数放进栈中,根据栈先进先出的特点,这时在从头遍历链表,每次比较一个结点的值和栈中的top的值,依次比较即可知道是否为回文结构;
coding
public static boolean isPalindrome1(Node head)//思路一;
{
Stack<Node> stack = new Stack<Node>();//取一个栈来存放遍历的数值;
Node cur = head;
while(cur!=null)
{
stack.push(cur);
cur = cur.next;
}
while(head!=null)
{
if(head.value != stack.pop().value)
{
return false;
}
head = head.next;
}
return true;
}
思路二:
沿用了思路一,但我们发现可以只把链表右侧的一半数据放入栈中,这样便可以节省一半的空间,来实现相同的功能。但是因为单链表的不可逆性,我们该如何知道何时将数据放入栈中才能实现放入右侧一半数据的目标呢
这里就要用到快慢指针的概念;就是取两个指针,都从头结点开始,快指针一次走两步,慢指针一次走一步,当快指针走完时,慢指针就来到了链表的中间节点。(但要注意,根据链表节点的奇偶个数,自己明确慢指针的具体位置,即当节点为偶数个数,慢指针最后会停在终点前一个还是后一个的问题。)
coding:
public static boolean isPalindrome2(Node head) {//思路二;
if(head == null||head.next == null)
{
return true;
}
Node fast = head;
Node slow = head.next;
while(fast.next != null && fast.next != null)
{
fast = fast.next.next;
slow = slow.next;
}
//跳出上面这个while循环时,slow指针就已经指向了链表的中间节点;
//如果节点总数为奇数时,指针在中点后一个,为偶数时,指针在中间两节点的后面一个;
Stack<Node> stack = new Stack<Node>();
while(slow!=null)
{
stack.push(slow);
slow = slow.next;
}
while(!stack.isEmpty())
{
if(head.value!=stack.pop().value)
return false;
head = head.next;
}
return true;
}
思路三:
当快指针走完时,慢指针停在中点的位置,这是我们让慢指针继续往后走,但是要让终点位置的指针指向null,后面的节点的next指针指向前一个结点;然后当慢指针走到最后一个节点时,再取一个新的头指针,头指针往后走,慢指针往前走,进行比对,但是在比对完成过后,慢指针要让每个结点的next指针恢复原位。(优点:没有任何额外空间)
coding:
public static boolean isPalindrome3(Node head)//思路三;
{
if(head == null||head.next == null)
return true;
Node fast = head;
Node slow = head.next;
while(fast.next != null && fast.next.next != null)
{
fast = fast.next.next;
slow = slow.next;
}
//将链表右侧反向;
Node n2 = slow.next;
Node n3 = null;
slow.next = null;
while(n2!=null)
{
n3 = n2.next;
n2.next = slow;
slow = n2;
n2 = n3;
}
//结束循环时,slow指针指向了最后一个节点,但因为右侧的链表指针都已经反向了,所以slow.next向前移动;
n2 = head;
n3 = slow;//用n3记录下右侧尾节点
boolean res = true;
while(slow != null && n2 != null)
{
if(slow.value!= n2.value)
{res = false;break;}
slow = slow.next;
n2 = n2.next;
}
//将右侧的链表节点回置为原来的样子;
slow = n3.next;
n3.next = null;
while(slow!=null)
{
n2 = slow.next;
slow.next = n3;
n3 = slow;
slow = n2;
}
return res;
}
将单向链表按值划分为左边小,中间相等,右边大的形式。
题目:
思路一:
申请一个Node型的数组,遍历链表,将链表中的每一个Node都存放进申请到的Node数组中,然后根据Node中value的值进行排序,把排好序的数组链接成一个新的链表即可;
思路二:
申请六个空间 分别为:SH,ST,EH,ET,BH,BT;依次代表小于部分的头和尾,等于部分的头和尾,大于部分的头和尾;然后遍历链表,遇到小于指定值的数(如果是第一次遇到:就把SH,ST都置为这个数)(如果是第二次遇到,就让尾指针ST指向这个数,并让SH指向ST); (如果是多次遇到后,就修改SH指针,让SH指向新的数,并把新的数置为ST); 等于和大于部分课都可以这样去思考;
最后让小于部分的尾指针指向等于部分的头指针,等于部分的尾指针指向大于部分的头指针;(这里需要考虑各个部分是否都有值的情况,即有可能不存在等于指定值的情况,这时候便需要来考虑)
coding:
public class Link_Sort_Number {
public static class Node{
public int value;
public Node next;
public Node(int data)
{
value = data;
}
}
public static Node listPartition(Node head,int pivot)//pivot为划分依据的值;
{
Node SH = null;
Node ST = null;
Node EH = null;
Node ET = null;
Node MH = null;
Node MT = null;
Node next = null;
while(head != null) {
next = head.next;
head.next = null;
if(head.value<pivot)
{
if(SH == null)
{
SH = ST = head;
}else{
ST.next = head;
ST = head;
}
}
else if(head.value==pivot)
{
if(EH == null)
{
EH = ET = head;
}else{
ET.next = head;
ET = head;
}
}
else
{
if(MH == null)
{
MH = MT = head;
}else{
MT.next = head;
MT = head;
}
}
head = next;
}
//接下来需要判断各个部分是否为空,并如何连接的问题;
if(SH != null)
{
ST.next = EH;
ET = (ET == null?ST:ET);
}
if(ET != null) {
ET.next = MH;
}
return SH != null? SH:(EH != null ?EH:MH);
}
public static void main(String[] args) {
Node head = new Node(1); //head链表不是一个回文链表
Node headx = new Node(1);//headx链表是一个回文链表;
Node head3 = head;
Node head4 = headx;
for(int i = 2;i<10;i++)
{
Node x = new Node((i*879-5)%14);
head3.next = x;
head3 = x;
//下面给回文链表赋值;
int add = i<=5? i:10-i;
Node y = new Node(add);
head4.next = y;
head4 = y;
}
head3.next = null;
head4.next = null;
/* while(headx!=null) {
System.out.println(headx.value);
headx = headx.next;
}*/
System.out.println("---------");
Node res = listPartition(headx,3);
while(res!=null) {
System.out.println(res.value);
res = res.next;
}
}
}
两个单链表相交的一系列问题
题目:
何谓相交:我们定义如果两个链表中有节点的地址相同,我们就称这两个节点相交;
记住,链表的每个节点都只会有一个next指针,所以两个无环单链表,从相交的那个节点开始,一直到null 一定是一直共有的;
我们先来考虑如何找出单链表中是否有环存在,并返回入环的第一个节点:
第一种方法:我们可以创建一个set 然后在链表中遍历,每次都把指针中的节点放入set中,并在每次放入时查看set中是否已经存在该节点,如存在,则该节点就是环的第一个入换节点;返回该结点即可;
第二种方法:利用快慢指针,都从头结点出发,快指针一次走两步,慢指针一次走一步,则快慢指针必在环中相遇(且在环中转的圈数不会超过两圈)如果快指针最后指向了null,则该链表中没有环;
在快慢指针相遇后,让快指针回到开头,慢指针原地不动,接下来两个指针都每次走一步,最后两指针相遇的节点即为第一个入环节点;
public static class Node{
public int value;
public Node next;
public Node(int data){
value = data;
}
}
//下面是找出有环单链表的第一个入环节点,如果链表无环,则返回null;
public static Node FindFirstLoopNode(Node head)
{
if(head == null||head.next == null||head.next.next == null)
return null;
Node fast = head.next.next;
Node slow = head.next;//定义快慢指针;
while(slow !=fast)
{
if(fast.next == null ||fast.next.next == null)//发现是一个无环单链表;
return null;
fast = fast.next.next;
slow = slow.next;
}
//相遇后,让快指针返回头部,快慢指针都每次走一步,相遇的那个节点就是入环的第一个节点;
fast = head;
while(fast!=slow)
{
fast = fast.next;
slow = slow.next;
}
return fast;
}
讨论两个无环链表的相交问题:
得到两个链表的头结点 head1,head2;
分别遍历两个链表,求出两个链表各自的表长和尾节点;
情况一:如果两个链表的尾节点不相同,则说明这两个链表不相交;
情况二:如果两链表的尾节点相同,则说明两链表相交,我们需要求出两链表相交的第一个节点,我们求出两个链表的长度之差,让表长长的链表先走abs.(head1.len-head2.len)步,之后两链表一起后移,第一次遇到相同节点时,返回这个节点即为两链表的第一个相交节点。
一个链表有环,另一个链表无环,则两者不可能相交;
//如果两个链表都无环,返回第一个相交的节点,如果不相交,就返回null;
public static Node noLoop(Node head1,Node head2)
{
int n = 0;
Node n1 = head1;
Node n2 = head2;
while(n1!=null)
{
n++;
n1 = n1.next;
}
while(n2!=null)
{
n--;
n2 = n2.next;
}
//上面n的值就是 head1的链表长度减去head2的链表长度;
if(n1 != n2)//代表head1 和head2 的尾节点不是一个点,则两链表不相交;
return null;
n1 = n>0? head1:head2;//把链表长度长的赋给n1;
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;
}
两个链表都有环:
public static Node bothLoop(Node head1,Node Loop1,Node head2,Node Loop2)//传入四个节点,分别为两个链表的头结点和入环的第一个节点;
{
int n = 0;
Node n1 = head1;
Node n2 = head2;
if(Loop1 == Loop2)//满足图2的情况;
{
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;
while(n1 != Loop1)
{
if(n1 == Loop2)
return Loop1;//满足图三;
n1 = n1.next;
}
return null;//满足图一;
}
}
调用函数
public static Node getIntersectNode(Node head1,Node head2){
if(head1 == null || head2 == null)
{
return null;
}
Node loop1 = getLoopNode(head1);
Node loop2 = getLoopNode(head2);
if(loop1 == null&&loop2 == null)//说明是两个无环连链表问题;
{
return noLoop(head1,head2);
}
if(loop1 != null &&loop2 != null)
{
return bothLoop(head1,loop1,head2,loop2);
}
return null;
}