链表,是一种线性表,与顺序表按照地址顺序存储不同的是,它的节点存储地址并不要求是顺序的,而是在链表的每个节点中保存着对下一个节点的地址的引用,就像是C++的指针一啊用的道理。
对于单链表的节点,我们可以用这个代码来定义:
public class ListClass<T> {
private T num;
private ListClass next;
public ListClass(T num, ListClass next) {
this.num = num;
this.next = next;
}
}
常见的面试题:
1.查找链表的中间值
一开始可能会简单的想,直接求出长度,再长度/2遍历一次就出来了,但是这很显然不是很理想,略微繁琐了些。正确(面试官想听的应该是:)使用快慢指针,快的遍历速度是慢的2倍,当快指针走到链表的尾巴时,慢指针就刚好是中间点。
//======================查找链表的中间值=========================================================
public static ListClass GetMid(ListClass head){
//我们使用快慢指针就好了
ListClass low = head;
ListClass fast = head;
while(fast != null && fast.next != null){
low = low.next;
fast = fast.next.next;
}
return low;
}
2. 判断链表是否有环,有的话求出入环口
我们可以使用HashMap<ListClass,Boolean>来保存每个节点在遍历中是否出现过了,如果出现了就有环,还可以直接得出那个入环口。但是创建hashMap会占用空间,并不是最优解,最优解应该还是快慢指针,判断是否有环,如果快指针和满指针重叠,就肯定是有环的。那么快慢指针怎么求出入环口呢?
设头节点到入环节点的距离为X,设入环节点到重合点的距离为Y,环的周长为R,那么慢指针走的路程为s1 = (X+Y)块指针走的路程为s2 = (X+Y+R); 而两者的关系为:s2 = 2*s1;
则 X+Y = R ; 也就是说,从重合点继续走到入环点的具体等于从头节点走到入环点
//======================判断链表是否有环,并且求出环的入口=========================================================
public static ListClass Position(ListClass head){
ListClass low = head;
ListClass fast = head;
while(fast != null && fast.next != null){
low = low.next;
fast = fast.next.next;
if(fast == low){
ListClass tmp = head;
if(tmp == fast){
return fast;
}
else{
while(true){
fast = fast.next;
tmp = tmp.next;
if(fast == tmp){
break;
}
}
return fast;
}
}
}
return null;
}
3.翻转链表
1->2->3->NULL ===> 3->2->1->NULL
//循环中变换
public static ListClass reverse1(ListClass head){
ListClass next = head;
ListClass pre = null;
while(head != null){
next = head.next;
head.next = pre;
pre = head;
head = next;
}
return pre;
}
//递归
public static ListClass reverse2(ListClass head){
if(head == null || head.next == null){
return head;
}
ListClass next = head.next;
ListClass now = reverse2(next);
next.next = head;
head.next = null;
return now;
}
4.求出两个链表是否相交,如果相交,求出相交的点
相交的两个单链表必然会在结尾出现重复的一段,所以我们只需要将先求出长的链表的长度n,短的m,让长的先走n-m步,再让他们同时走,走到某个节点相同就额是相交的点。
public static ListClass Jiao(ListClass listClass1,ListClass listClass2){
if(listClass1 == null || listClass2 == null){
return null;
}
int len1 = 0;
ListClass tmp1 = listClass1;
while(tmp1.next != null){
tmp1 = tmp1.next;
len1++;
}
ListClass tmp2 = listClass2;
int len2 = 0;
while(tmp2.next != null){
tmp2 = tmp2.next;
len2++;
}
int chang = Math.max(len1,len2);
int duan = Math.min(len1,len2);
ListClass longger = len2 > len1 ? listClass2 : listClass1;
ListClass shorter = len2 > len1 ? listClass1 : listClass2;
int cha = chang - duan;
while(cha -- != 0){
longger = longger.next;
}
while(longger != null && shorter != null){
if(longger == shorter){
return shorter;
}
longger = longger.next;
shorter = shorter.next;
}
return null;
}
5.链表的插入排序
public static ListClass insertSort(ListClass head){
if(head == null || head.next == null){
return head;
}
ListClass pre = head;
ListClass start = new ListClass(-999,null);
start.next = head;
ListClass next = head.next;
while(next != null){
if(pre.num > next.num){
pre.next = next.next;
ListClass tmp = start;
ListClass tmpNext = start.next;
while(tmpNext.num < next.num){
tmp = tmpNext;
tmpNext = tmpNext.next;
}
next.next = tmpNext;
tmp.next = next;
next = pre.next;
}
else{
pre = pre.next;
next = next.next;
}
}
return start.next;
}
6.链表的插入排序
//======================链表快速排序=========================================================
public static ListClass quickSort(ListClass begin, ListClass end) {
//判断为空,判断是不是只有一个节点
if (begin == null || end == null || begin == end)
return begin;
//从第一个节点和第一个节点的后面一个几点
//begin指向的是当前遍历到的最后一个<= nMidValue的节点
ListClass first = begin;
ListClass second = begin.next;
int key = begin.num;
//结束条件,second到最后了
while (second != end.next && second != null) {//结束条件
//一直往后寻找<=nMidValue的节点,然后与fir的后继节点交换
if (second.num < key) {
first = first.next;
//判断一下,避免后面的数比第一个数小,不用换的局面
if (first != second) {
int temp = first.num;
first.num = second.num;
second.num = temp;
}
}
second = second.next;
}
//判断,有些情况是不用换的,提升性能
if (begin != first) {
int temp = begin.num;
begin.num = first.num;
first.num = temp;
}
//前部分递归
quickSort(begin, first);
//后部分递归
quickSort(first.next, end);
return begin;
}