本文列举出了7个链表常见的面试题,其他面试题请阅读下一篇博客
面试题1:删除非尾节点
删除非尾节点,只告诉一个当前节点的位置,将该位置的节点删除。
解题思路:
删除该位置的节点,由于我们不知道它的前一个节点的位置,不能直接将它的前一个节点与它的后一个节点连接起来。
因此,我们需要用另一种方法:
将该节点的后一个节点的元素赋值给该节点,然后删除该节点的后一个节点。
void EraseNotTail(pLinkNode pos) { assert(pos); pLinkNode del = NULL; pos->data = pos->next->data; //后一个节点的元素的值赋给当前节点的值 del = pos->next; //当前节点的后一个节点为要删除的元素 pos->next = pos->next->next; free(del); // 删除该节点 del = NULL; }
面试题2:逆序单链表
解题思路:
将单链表逆序的基本思想是将第一个节点的next置成NULL,然后将它的后一个节点指向它,依次循环直到将最后一个节点指向它的前一个节点。
void ReverseList(pList* pHead) { assert(pHead); pLinkNode cur = *pHead; pLinkNode prev = NULL; pLinkNode NewHead = NULL; while (cur) { prev = cur; cur = cur->next; prev->next = NewHead; NewHead = prev; } *pHead = NewHead; }
面试:3:排序单链表
解题思路:
排序链表可以用冒泡排序的思想,从前往后遍历链表,要是前一个元素比后一个元素大,那么就交换两个元素,遍历一次的结果 是将链表中最大的元素放在最后。
void BubbleSort(pList * pHead) { assert(pHead); pLinkNode cur = *pHead; pLinkNode end = NULL; DataType tmp = 0; while (cur != end) { while (cur && cur->next != end) { if (cur->data > cur->next->data) { tmp = cur->data; cur->data = cur->next->data; cur->next->data = tmp; } //将较大元素放在后面,较小元素放到前面 cur = cur->next; } //遍历一次完成,将最大元素放在链表末尾 end = cur; cur = *pHead; } }
面试题4:在当前节点前插入一个数据x
解题思路:
基本思想是将x插入到当前节点之后,然后交换两个节点的元素。
void InsertFrontNode(pLinkNode pos, DataType x) { assert(pos); pLinkNode NewNode = BuyNode(x); DataType tmp = 0; NewNode->next = pos->next; pos->next = NewNode; //将要插入的元素插入到当前节点的后面 tmp = NewNode->data;//交换两元素 NewNode->data = pos->data; pos->data = tmp; }
面试题5:合并两个有序列表
解题思路:
基本思想是首先确定一个合并以后的头结点,比较两个链表的第一个元素,较小的元素所在的节点为头结点。然后比较两个链表剩余节点的第一个节点,将较小的一个连在头结点的后面,再次比较剩余的节点,然后依次将较小的节点连在新链表的后面,直到有一个链表为空。将另一个链表的剩余节点全都连在新链表上。
有两种方法可以实现:
一、非递归的方法:
pLinkNode Merge(pList l1, pList l2) { pList NewHead = NULL; pLinkNode cur = NULL; //若有一个链表为空,返回不是空链表的那个链表,若都为空,返回任意一个 if (l1 == NULL || l2 == NULL) { if (l1) return l1; else return l2; } if (l1->data < l2->data) //若l1的第一个元素比l2的第一个元素小,那么l1的第一个节点为头节点 { NewHead = l1; l1 = l1->next; } else //若l2的第一个元素比l1的第一个元素小,那么l2的第一个节点为头节点 { //若两个元素相等则任意一个做头节点都可以,这里让l2的元素做头节点 NewHead = l2; l2 = l2->next; } cur = NewHead; while (l1 && l2) { if (l1->data < l2->data) { cur->next = l1; l1 = l1->next; } else { cur->next = l2; l2 = l2->next; } //将较小的元素连在新链表的后面 cur = cur->next; } if (l1) { cur->next = l1; // 若l1不为空,将l1剩余的元素连接在新链表的后面 } else { cur->next = l2; } //若l2不为空,将l2剩余的元素连接在新链表的后面 return NewHead; }
二、递归的方法:
因为每次都是将两个链表上的较小的节点连在新链表的后面,因此可以用递归方法实现
pLinkNode _Merge(pList l1, pList l2) { pList NewHead = NULL; pLinkNode cur = NULL; if (l1 == NULL || l2 == NULL) { if (l1) return l1; else return l2; } if (l1->data < l2->data) { NewHead = l1; NewHead->next = _Merge(l1->next, l2); } else { NewHead = l2; NewHead->next = _Merge(l1, l2->next); } return NewHead; }
面试题6:查找中间节点
解题思路:
用两个指针同时从头节点开始向后遍历,一个快指针一次走两步,另一个慢指针一次走一步,直快指针走到结尾,则指针所在的位置就是中间节点。
pLinkNode FindMidNode(pList head) { pLinkNode fast = head; pLinkNode slow = head; while (fast && fast->next) { fast = fast->next->next; //快指针一次走两步 slow = slow->next; //慢指针一次走一步 } return slow; //返回中间节点 }
面试题7: 删除单链表的倒数第k个节点(k > 1 && k < 链表的总长度) 时间复杂度O(N)
解题思路:
要求遍历一遍删除链表中倒数第k各节点,可以用两个指针,第一个指针先走k-1步,然后第一个指针和第二个指针一起走,当第二个指针到达链表末尾时,第一个指针所在的位置就是倒数第k各节点。
然后根据删除非尾节点的方法将它删除(删除非尾节点的方法可参考面试题1)。
void DelKNode(pList *pHead, int k) { assert(k > 1); pLinkNode l1 = *pHead; pLinkNode l2 = *pHead; pLinkNode del = NULL; while (--k && l2->next) //第二个指针先走k-1步 { l2 = l2->next; } if (l2->next == NULL) //k>=链表总长度 { return; } while (l2->next) //两个指针一起走 { l2 = l2->next; l1 = l1->next; } del = l1->next; l1->data = l1->next->data; l1->next = l1->next->next; free(del); //删除倒数第k各节点 del = NULL; }