链表常见问题解析
找到链表的倒数第n个结点
方法1:
从链表的第一个结点开始,统计当前结点后面的结点个数。如果后面结点的个数小于n-1,那么算法结束并返回消息"链表中的结点个数不足"。如果数量大于n-1,则移动到下一个结点。重复该过程直至当前结点后面的结点个数等于n-1。
[时间复杂度O(n^2),空间复杂度O(1)]
方法2:
该方法需要新建一个散列表,表结构是<结点的位置, 结点地址>。当遍历链表时可以得到链表的长度记为M,求解这个问题只需要返回散列表中主键为M-n+1的值。
[时间复杂度O(M),空间复杂度O(M)]
方法3:
从表头结点开始遍历链表得到链表的长度M,然后在从表头开始再一次遍历得到第M-n+1个结点。
[时间复杂度O(M)+O(n)=O(n),空间复杂度O(1)]
方法4:
使用连个指针pNthNode和pTemp,首先两个指针都指向链表的表头结点。仅当pTemp沿着链表进行了n次移动后pNthNode才开始移动。然后两个指针同时移动直至pTemp到达表尾,这时pNthNode所指的结点就是所求的结点。也就是链表的倒数第n个结点。
[时间复杂度O(n),空间复杂度O(1)]
ListNode nthNodeFromEnd(ListNode head, int nthNode){
ListNode pTemp = head;
ListNode pNthNode = null;
for(int count=1; count<nthNode;count++){
if(pTemp!=null){
pTemp = pTemp.getNext();
}
}
while (pTemp!=null){
if(pNthNode == null){
pNthNode = head;
}else {
pNthNode = pNthNode.getNext();
}
pTemp = pTemp.getNext();
}
if(pNthNode!=null){
return pNthNode;
}
return null;
}
判定给定的链表是以NULL结尾,还是形成一个环
方法1:
从表头结点开始,逐个遍历链表中的每个结点。对于每个结点,检查该结点的地址是否存在于散列表中。如果存在则表明当前范围的结点已经被访问过了。出现这种情况只能是因为给定的链表中存在环。如果散列表中没有当前结点,那么把该地址插入散列表中。
[时间复杂度O(n),空间复杂度O(n)]
方法2:
Floyd环判定算法。该方法使用两个在链表中具有不同移动速度的指针。一旦他们进入环就会相遇。这个判定方法的准确性在于,快速移动指针和慢速移动指针将会指向同一位置的唯一可能情况就是整个或者部分链表时一个环。
[时间复杂度O(n),空间复杂度O(1)]
boolean doesLinedListContainsLoop(ListNode head){
if(head == null){
return false;
}
ListNode slowPtr = head;
ListNode fastPtr = head;
while (fastPtr.getNext()!=null && fastPtr.getNext().getNext()!=null){
slowPtr = slowPtr.getNext();
fastPtr = fastPtr.getNext().getNext();
if(slowPtr == fastPtr){
return true;
}
}
return false;
}
判定给定的链表是以NULL结尾,还是形成一个环,如果链表中存在环,找到环的起始结点
Floyd环判定算法。
1.该方法使用两个在链表中具有不同移动速度的指针。一旦他们进入环就会相遇。这个判定方法的准确性在于,快速移动指针和慢速移动指针将会指向同一位置的唯一可能情况就是整个或者部分链表时一个环。
2.当快速移动的指针和慢速移动的指针相遇后,让慢速指针从表头开始移动,而快速指针从相遇位置开始移动,快速指针和慢速指针每次都移动一个结点,当它们再次相的位置就是环的起始结点。
[时间复杂度O(n),空间复杂度O(1)]
ListNode findBeginOfLoop(ListNode head) {
ListNode slowPtr = head;
ListNode fastPtr = head;
boolean loopExists = false;
if (head == null) {
return null;
}
while (fastPtr.getNext() != null && fastPtr.getNext().getNext() != null) {
slowPtr = slowPtr.getNext();
fastPtr = fastPtr.getNext().getNext();
if (slowPtr == fastPtr) {
loopExists = true;
break;
}
}
if (loopExists == true) { //环存在
slowPtr = head;
while (slowPtr != fastPtr) {
fastPtr = fastPtr.getNext();
slowPtr = slowPtr.getNext();
}
return slowPtr;
}
return null;
}
判定给定的链表是以NULL结尾,还是形成一个环,如果链表中存在环,返回环的长度
当快速指针和慢速指针相遇后,保持慢速指针不动,快速指针每次只移动一个结点,当他们再次相遇时,快速指针刚好比慢速指针都走了一个环的长度。
[时间复杂度O(n),空间复杂度O(1)]
int findLoopLength(ListNode head) {
ListNode slowPtr = head;
ListNode fastPtr = head;
boolean loopExists = false;
int counter = 0;
if (head == null) {
return 0;
}
while (fastPtr.getNext() != null && fastPtr.getNext().getNext() != null) {
slowPtr = slowPtr.getNext();
fastPtr = fastPtr.getNext().getNext();
if (slowPtr == fastPtr) {
loopExists = true;
break;
}
}
if (loopExists == true) {
fastPtr = fastPtr.getNext();
while (slowPtr != fastPtr) {
fastPtr = fastPtr.getNext();
counter++;
}
}
return counter;
}
逆置单向链表
[时间复杂度O(n),空间复杂度O(1)]
ListNode reverseList(ListNode head) {
ListNode temp = null;
ListNode nextNode = null;
while (head != null) {
nextNode = head.getNext();
head.setNext(temp);
temp = head;
head = nextNode;
}
return temp;
}
假设两个单向链表在某个结点相交后,成为一个单向链表。两个链表的表头结点是已知的,但是相交的结点未知。并且两个链表的结点数也可能不同。请设计算法找到两个链表的合并点
获取两个链表的长度m,n.计算两个长度的差d。从较长链表的表头开始,移动d步。再两个链表开始同时移动。直至出现两个后继指针值相等的情况。
[时间复杂度O(max(m,n),空间复杂度O(1)]
ListNode findIntersectingNode(ListNode list1, ListNode list2) {
int len1 = 0;
int len2 = 0;
int diff = 0;
ListNode head1 = list1;
ListNode head2 = list2;
while (head1 != null) {
len1++;
head1 = head1.getNext();
}
while (head2 != null) {
len2++;
head2 = head2.getNext();
}
if (len1 < len2) {
head1 = list2;
head2 = list1;
diff = len2 - len1;
} else {
head1 = list1;
head2 = list2;
diff = len1 - len2;
}
for (int i = 0; i < diff; i++) {
head1 = head1.getNext();
}
while (head1 != null && head2 != null) {
if (head1 == head2) {
return head1;
}
head1 = head1.getNext();
head2 = head2.getNext();
}
return null;
}
检查链表的长度是奇数还是偶数
使用一个在链表中每次向后移动两个结点的指针。最后如果指针值为NULL。那么链表长度为偶数,否则指针指向表尾结点,链表长度为奇数
[时间复杂度O(max(m,n),空间复杂度O(1)]
int isLinkedListLengthEven(ListNode head) {
while (head != null && head.getNext() != null) {
head = head.getNext().getNext();
}
if (head == null) {
return 0;
} else {
return 1;
}
}