1. 链表属于比较基础的数据结构,看起来简单,实际上写起来并不简单,考察的是面试者的细心。
2. 下面以C++语言实现一个单链表,其中囊括了大部分的单链表面试题。
#include <iostream>
using namespace std;
class MyList {
struct Node;
public:
//1. 构造函数,让head的value永远为NULL,head作为哨兵节点
MyList() {
head = new Node(NULL);
head->next = nullptr;
}
//2. 需要释放内存,避免内存泄漏。下面提供了两种析构链表的方法,作为对链表的思考试验
~MyList() {
方法一:先删除所有的head->next节点,最后删除head节点
//if (head) {
// while (head->next) {
// Node* deleteNode = head->next;
// head->next = deleteNode->next;
// cout << "删除:" << deleteNode->data << endl;
// delete deleteNode;
// }
// cout << "删除:head" << endl;
// delete head;
//}
//方法二:从头结点开始删除,依次删除所有节点,也就是每次删除的都是头结点
while (head) {
Node* deleteNode = head;
head = head->next;
cout << "删除:" << deleteNode->data << endl;
delete deleteNode;
}
}
//3.在链表的尾部插入节点,从这里可以看出有一个哨兵头结点的好处,那就是不用对头结点做特殊处理
void push_back(int value) {
Node* tmp = head;
//找到尾部
while (tmp->next != nullptr) {
tmp = tmp->next;
}
Node* insertNode = new Node(value);
tmp->next = insertNode;
}
//4. 每次都在链表的头部插入节点,即每次在head之后进行插入
void push_front(int value) {
Node* insertNode = new Node(value);
insertNode->next = head->next;
head->next = insertNode;
}
//5. 在链表的指定位置插入节点,例如原链表为1,2,3,4,在第2个位置后插入6,变为1,2,6,3,4
void insertNode(int index, int value) {
Node* insertNode = new Node(value);
Node* tmp = head;
for (int i = 0; i < index; i++) {
tmp = tmp->next;
}
insertNode->next = tmp->next;
tmp->next = insertNode;
}
//6. 删除指定位置的节点
void deleteNode(int index) {
Node* tmp = head;
Node* prev = head;
for (int i = 0; i < index; i++) {
prev = tmp;
tmp = tmp->next;
}
prev->next = tmp->next;
delete tmp;
}
//7. 删除链表中倒数第N个节点,查找倒数第N个节点同理
void deleteNode2(int index) {
Node* firstNode = head;
Node* secondeNode = head;
Node* prev = head;
for (int i = 0; i < index; i++) {
firstNode = firstNode->next;
}
while (firstNode) {
prev = secondeNode;
firstNode = firstNode->next;
secondeNode = secondeNode->next;
}
prev->next = secondeNode->next;
delete secondeNode;
}
//6. 链表的反转
void reverseList() {
Node* cur = head->next;
Node* prev = nullptr;
Node* next = cur;
while (cur) {
if (!cur->next) {
head->next = cur;
}
next = cur->next;
cur->next = prev;
prev = cur;
cur = next;
}
}
//7. 链表中环的检测,以及环的入口,还需要仔细思考下,本体考察快慢指针
bool checkCircle() {
Node* fast = head;
Node* slow = head;
Node* crossPoint = nullptr;
Node* entry = nullptr;
while (fast->next) {
if (fast == slow) {
crossPoint = slow;
fast = head;
while (fast) {
if (fast == slow) {
entry = fast;
return true;
}
fast = fast->next;
slow = slow->next;
}
}
fast = fast->next->next;
slow = slow->next;
}
return false;
}
//8. 两个有序链表的合并
void merge(MyList* list);
//9. 求链表的中间节点,本题的解法明显是对快慢指针的考察,快节点每次走两步,
//慢节点每次走一步,那么慢节点就是我们的中间节点,同理求数组的中间节点
//需要注意的是,需要注意各种判断条件,链表考察的就是细心
int midle() {
Node* fast = head;
Node* slow = head;
while (fast) {
if (fast->next) {
fast = fast->next->next;
}
else {
break;
}
slow = slow->next;
}
return slow->data;
}
//10. 打印链表
void show() {
Node* loop = head->next;
while (loop) {
cout << loop->data << endl;
loop = loop->next;
}
}
private:
struct Node
{
int data;
Node* next;
Node(int val):data(val),next(nullptr){}
};
private:
//链表头,注意头结点的值永远为空,目的是方便链表的操作
Node* head;
};
int main() {
MyList* list = new MyList();
list->push_back(10);
list->push_back(20);
list->push_back(30);
list->push_back(40);
//list->push_back(50);
list->show();
int mid = list->midle();
cout << "mid:" << mid << endl;
//list->reverseList();
//list->insertNode(2, 25);
//list->deleteNode(3);
//list->deleteNode2(2);
//list->push_front(9);
//list->push_front(8);
list->show();
delete list;
return 0;
}