目录
写在前面:这本书不是圣经,一定要自己理解思路后独立完成代码 链表
【面试题6】:从尾到头打印链表
题目:输入一个链表的头结点,从尾巴到头反过来打印出每个节点的值。
#include <iostream>
#include <stack>
using namespace std;
//链表节点
struct ListNode {
int m_nValue; //数据域
ListNode* m_pNext;//指针域
};
//往链表结尾插入一个节点
void insert_to_tail(ListNode* &head, int x) {//由于要改变头结点,所以这里是传引用,比原来的传双重指针更好理解
ListNode* pNew = new ListNode;
pNew->m_nValue = x;
pNew->m_pNext = NULL;
if (head == NULL) {
head = pNew;
}
else {
ListNode* p = head;//由于是传引用,所以要避免直接修改head,故创建临时变量p
while (p->m_pNext != NULL) {
p = p->m_pNext;
}
p->m_pNext = pNew;
}
}
//display
void display(ListNode* head) {
while (head!= NULL) {
cout << head->m_nValue<<" ";
head = head->m_pNext;
}
cout << endl;
}
//从尾到头打印链表
void print_tail_to_head(ListNode* head) {
stack<ListNode*>nodes;
while (head != NULL) {
nodes.push(head);
head = head->m_pNext;
}
while (!nodes.empty()) {
cout << nodes.top()->m_nValue << " ";
nodes.pop();
}
}
int main() {
ListNode* head = NULL;//创建头节点
insert_to_tail(head, 1);
insert_to_tail(head, 2);
insert_to_tail(head, 3);
insert_to_tail(head, 4);
insert_to_tail(head, 5);
print_tail_to_head(head);
return 0;
}
【面试题18】:删除链表的节点
题目一:在O(1)时间内删除链表的节点
给定单向链表的头指针和下一个节点指针,定义一个函数在O(1)时间内删除该节点。链表节点与函数的定义如下。
常规做法:从链表的头结点开始遍历,顺序遍历查找要删除的节点,并在链表中删除该节点。复杂度O(n)
void RemoveNode(ListNode* &head, ListNode* p_deleted) {
if (head == NULL || p_deleted==NULL)
return;
ListNode* p = head;
if (p == p_deleted) {
head = head->m_pNext;
delete p;
}
else {
while (p->m_pNext != NULL && p->m_pNext != p_deleted) {
p = p->m_pNext;
}
if (p->m_pNext != NULL && p->m_pNext == p_deleted) {
p->m_pNext = p->m_pNext->m_pNext;
delete p_deleted;
}
}
}
令一种做法:
//假设p_deleted 是链表中的节点
void RemoveNode(ListNode* &head, ListNode* p_deleted) {
if (head == NULL || p_deleted==NULL)
return;
//不是尾节点(说明至少有两个节点)
if (p_deleted->m_pNext != NULL) {
ListNode* p = p_deleted->m_pNext;
p_deleted->m_nValue = p_deleted->m_pNext->m_nValue;
p_deleted->m_pNext = p_deleted->m_pNext->m_pNext;
delete p;
}
else if (p_deleted == head) {//是尾节点还是头结点
head = NULL;
delete p_deleted;
}
else //是尾节点,不是头结点
{
ListNode*p = head;
while (p->m_pNext != p_deleted)
p = p->m_pNext;
p->m_pNext = NULL;
delete p_deleted;
}
}
题目二:删除链表中重复的节点
void delete_duplication(ListNode* &head) {
if (head == NULL)
return;
ListNode*p = head;
ListNode* pre = NULL;
ListNode* pcur = p;
while (pcur != NULL) {
ListNode* pnext = pcur->m_pNext;
bool need_deleted = false;
if (pnext != NULL && pcur->m_nValue == pnext->m_nValue) {
need_deleted = true;
}
if (!need_deleted) {
pre = pcur;
pcur = pcur->m_pNext;
}
else {
int value = pcur->m_nValue;
ListNode* p_deleted = pcur;;
while (p_deleted!=NULL && p_deleted->m_nValue==value) {
pcur = pcur->m_pNext;
delete p_deleted;
p_deleted = pcur;
}
if (pre == NULL)
head = pcur;
else
pre->m_pNext = pcur;
}
}
}
题目三:去重
void delete_duplication_2(ListNode* &head) {
ListNode*p = head;
ListNode*pre = NULL;
ListNode*pcur = p;
while (pcur!=NULL) {
ListNode*pnext = pcur->m_pNext;
bool need_deleted = false;
if (pnext != NULL && pcur->m_nValue == pnext->m_nValue) {
need_deleted = true;
}
if (!need_deleted) {
pre = pcur;
pcur = pcur->m_pNext;
}
else {
ListNode*p_delete = pcur;
pcur = pnext;
delete p_delete;
if (pre == NULL)
head = pcur;
else
pre->m_pNext = pcur;
}
}
}
【面试题22】:链表中倒数第K个节点
ListNode* FindKthToTail(ListNode*head, unsigned int k) {
if (head == NULL || k == 0)
return nullptr;
ListNode* p1 = head;
ListNode* p2 = p1;
for (unsigned int i = 1; i < k; i++) {
p2 = p2->m_pNext;
if (p2 == NULL)
return NULL;
}
while (p2->m_pNext!= NULL) {
p1 = p1->m_pNext;
p2 = p2->m_pNext;
}
return p1;
}
【面试题24】:反转链表
void reserve(ListNode* &head) {//由于要修改头结点,故传引用
if (head == NULL)
return;
ListNode* pre = NULL;
ListNode* pcur = head;
ListNode* pnext = pcur->m_pNext;
while (pcur!= NULL) {
pcur->m_pNext = pre;
pre = pcur;
pcur = pnext;
if(pnext!=NULL)
pnext = pnext->m_pNext;
}
head = pre;//注意退出循环时的条件,此时pre才是指向最后一个元素
}
【面试题25】:合并两个排序的链表
ListNode* merge(ListNode* head1, ListNode* head2) {
if (head1 == NULL)
return head2;
if (head2 == NULL)
return head1;
ListNode* merge_head = NULL;
if (head1->m_nValue < head2->m_nValue) {
merge_head = head1;
merge_head->m_pNext = merge(head1->m_pNext, head2);
}
else {
merge_head = head2;
merge_head->m_pNext = merge(head1, head2->m_pNext);
}
return merge_head;
}
【面试题62】:圆桌中最后剩下的数字
题目:0,1,2…,n-1这n个数字排成一个圆圈,从数字0开始,每次从这个圆圈里删除第m个数字。求出这个圆圈里剩下的最后一个数字。
经典解法:用环形链表模拟圆圈。
int lastRemaining(unsigned n, unsigned m) {
list<int>numbers;
for (int i = 0; i < n; i++)
numbers.push_back(i);
list<int>::iterator cur = numbers.begin();
while (numbers.size() > 1) {
for (int i = 0; i < m - 1; i++) {
cur++; //注意end是超尾,所以先增加再判断
if (cur == numbers.end())
cur = numbers.begin();
}
list<int>::iterator next =++cur;
if (next == numbers.end())
next = numbers.begin();
cur--;
numbers.erase(cur);//迭代器cur指向的内容被删除了,但迭代器对象本身还在
cur = next;
}
return *cur;
}