1、移除链表元素
第一种方法,使用不带虚你头结点的链表
class Solution {
public:
ListNode* removeElements(ListNode* head, int val) {
// 删除头结点
while (head != nullptr && head->val == val) {
ListNode* tmp = head;
head = head->next;
delete tmp;
}
// 删除非头结点
ListNode* cur = head;
while (cur != nullptr && cur->next!= nullptr) {
if (cur->next->val == val) {
ListNode* tmp = cur->next;
cur->next = cur->next->next;
delete tmp;
} else {
cur = cur->next;
}
}
return head;
}
};
第二种方法,使用带虚拟头结点的链表
class Solution {
public:
ListNode* removeElements(ListNode* head, int val) {
ListNode* dummyHead = new ListNode(0);
dummyHead->next = head; // 把不带虚拟头的转化为带虚拟头的
ListNode* cur = dummyHead;
while (cur->next != nullptr) {
if(cur->next->val == val) {
ListNode* tmp = cur->next;
cur->next = cur->next->next;
delete tmp;
} else {
cur = cur->next;
}
}
head = dummyHead->next;
delete dummyHead;
return head;
}
};
2、设计链表
使用带虚拟头结点的链表,这样更方便
使用C++去实现:
#include <iostream>
using namespace std;
class MyLinkedList {
public:
// 定义链表节点结构体
struct LinkedNode { //类中可以定义结构体,也可以定义类
int val;
LinkedNode* next;
explicit LinkedNode(int val):val(val), next(nullptr){} //结构体的构造函数
};
// 初始化链表
MyLinkedList() { //类的构造函数
_dummyHead = new LinkedNode(0); // 定义虚拟头结点
_size = 0; //单下划线开头的变量可以看成是私有变量的特征
}
// 获取到第index个节点数值,如果index是非法数值直接返回-1, 注意index是从0开始的,第0个节点就是头结点,也就是虚拟头结点的下一个
int get(int index) {
if (index > (_size - 1) || index < 0) //非法位置
return -1;
LinkedNode* cur = _dummyHead->next; //头结点保持不动
while(index--) // 如果--index 就会陷入死循环
cur = cur->next;
return cur->val;
}
// 头插法
void addAtHead(int val) {
LinkedNode* newNode = new LinkedNode(val);
newNode->next = _dummyHead->next;
_dummyHead->next = newNode;
_size++;
}
// 尾插法
void addAtTail(int val) {
LinkedNode* newNode = new LinkedNode(val); //当新建一个结点时,它的next指针已经指向了nullptr
LinkedNode* cur = _dummyHead;
while(cur->next) //找到最后的一个结点
cur = cur->next;
cur->next = newNode;
_size++;
}
// 在第index个节点之前插入一个新节点
void addAtIndex(int index, int val) {
// 如果index大于链表的长度,则返回空
if(index > _size)
return;
// 如果index小于0,则在头部插入节点
if(index < 0)
index = 0;
LinkedNode* newNode = new LinkedNode(val);
LinkedNode* cur = _dummyHead;
while(index--) //找到前驱结点
cur = cur->next;
newNode->next = cur->next;
cur->next = newNode;
_size++;
}
// 删除第index个节点
void deleteAtIndex(int index) {
if (index >= _size || index < 0)
return;
LinkedNode* cur = _dummyHead;
while(index--)
cur = cur ->next;
LinkedNode* tmp = cur->next;
cur->next = cur->next->next;
delete tmp;
tmp = nullptr; //如果确定之后不会再使用tmp,也可以不设置其为nullptr
_size--;
}
// 打印链表
void printLinkedList() {
LinkedNode* cur = _dummyHead; //由于在类里,所以不用再传结构体指针
while (cur->next) {
cout << cur->next->val << " -> ";
cur = cur->next;
}
cout << "nullptr" << endl;
}
private:
int _size; //链表长度,不包括虚拟头
LinkedNode* _dummyHead; //声明一个指向虚拟头的结构体指针
};
int main() {
MyLinkedList LinkedList;
for (int i = 1; i <= 5; ++i)
LinkedList.addAtTail(i);
LinkedList.printLinkedList();
}
delete
命令指示释放了tmp
指针原本所指的那部分内存,被delete
后的指针tmp
的值(地址)并非就是NULL
,而是随机值。也就是被delete
后,如果不再加上一句tmp=nullptr
,tmp
会成为乱指的野指针,如果之后的程序不小心使用了tmp
,会指向难以预想的内存空间。
使用C语言去实现:
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
typedef int E;
// 带头结点链表
typedef struct ListNode {
E val;
struct ListNode *next;
} ListNode, *Node; //ListNode是结点,Node是链表头指针
void initList(Node head) {
head->next = NULL;
}
bool insertList(Node head, E val, int index) {
if (index < 1) { //限定左边界
printf("插入失败\n");
return false;
}
while (--index) {
head = head->next; //找到要插入位序的前驱节点
if (!head) { //限定右边界
printf("插入失败\n");
return false;
}
}
Node node = malloc(sizeof(ListNode));
if (!node) {
printf("内存分配失败\n");
return false;
}
node->val = val;
node->next = head->next;
head->next = node;
return true;
}
//头插法
bool insertFrontList(Node head, E val) {
Node node = malloc(sizeof(ListNode));
if (!node) {
printf("内存分配失败\n");
return false;
}
node->val = val;
node->next = head->next;
head->next = node;
return true;
}
//尾插法
bool insertEndList(Node head, E val) {
Node node = malloc(sizeof(ListNode));
if (!node) {
printf("内存分配失败\n");
return false;
}
while (head->next)
head = head->next;
node->val = val;
node->next = NULL;
head->next = node;
return true;
}
bool deleteList(Node head, int index) {
if (index < 1) { //限定左边界
printf("删除失败\n");
return false;
}
while (--index) {
head = head->next; //找到要删除位序的前驱节点
if (!head->next) { //限定右边界
printf("删除失败\n");
return false;
}
}
if(head->next == NULL) {
printf("删除失败\n");
return false;
}
Node tmp = head->next;
head->next = head->next->next;
free(tmp);
return true;
}
E *getList(Node head, int index) { //这里返回指针是为了防止隐式类型转换
if (index < 1) { //限定左边界
printf("获取失败\n");
return NULL;
}
do { //因为头结点不存放数据,所以用do...while
head = head->next; //这里head指向该位序的节点
if (!head) {
printf("获取失败\n");
return NULL;
}
} while (--index);
return &head->val;
}
int findList(Node head, E val) {
head = head->next; //带结点和不带结点的区别
int i = 1;
while (head) {
if (head->val == val)
return i;
head = head->next;
i++;
}
return -1;
}
int sizeList(Node head) {
int i = 0;
while (head->next) {
head = head->next;
i++;
}
return i;
}
void printList(Node head) {
while (head->next) {
head = head->next;
printf("%d -> ", head->val);
}
printf("NULL\n");
}
int main() {
ListNode head;
initList(&head);
for (int i = 1; i <= 2; ++i)
insertList(&head, i * 100, i);
deleteList(&head, 1);
printList(&head);
}
3、反转链表
第一种方法,双指针,先用C来实现
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
typedef struct ListNode {
int val;
struct ListNode *next;
} ListNode, *Node;
void initList(Node *head) {
*head = NULL;
}
bool insertEndList(Node *head, int val) {
Node node = malloc(sizeof(ListNode));
if (!node) {
printf("内存分配失败\n");
return false;
}
node->val = val;
node->next = NULL;
if (!*head) {
*head = node;
} else {
Node tmp = *head; //保持头结点不动
while (tmp->next)
tmp = tmp->next;
tmp->next = node;
}
return true;
}
Node reverseList(Node head) { //由于改变了头指针指向的内容,所以只能返回指针类型
Node node = NULL;
Node tmp;
while (head) {
tmp = head->next;
head->next = node;
node = head;
head = tmp;
}
return node;
}
void printList(Node head) { //只读取,不改变结构体指针的指向
while (head) {
printf("%d -> ", head->val);
head = head->next;
}
printf("NULL\n");
}
int main() {
Node head;
initList(&head);
for (int i = 1; i <= 5; ++i)
insertEndList(&head, i);
printList(head);
Node node = reverseList(head);
printList(node);
}
再用C++来实现
#include <iostream>
using namespace std;
struct ListNode {
int val;
ListNode *next;
explicit ListNode(int val) : val(val), next(nullptr) {}
};
class Solution {
public:
ListNode *reverseList(ListNode *head) {
ListNode *temp; // 保存cur的下一个节点
ListNode *cur = head;
ListNode *pre = nullptr;
while (cur) {
temp = cur->next; // 保存一下 cur的下一个节点,因为接下来要改变cur->next
cur->next = pre; // 翻转操作
// 更新pre 和 cur指针
pre = cur;
cur = temp;
}
return pre; //这里其实可以直接移动head
}
};
void printList(ListNode* head) {
ListNode* cur = head;
while (cur) {
cout << cur->val << " -> ";
cur = cur->next;
}
cout << "nullptr" << endl;
}
int main() {
// 创建一个简单的链表: 1 -> 2 -> 3 -> 4 -> 5
ListNode* head = new ListNode(1);
head->next = new ListNode(2);
head->next->next = new ListNode(3);
head->next->next->next = new ListNode(4);
head->next->next->next->next = new ListNode(5);
printList(head);
Solution solution;
ListNode *node = solution.reverseList(head);
printList(node);
}
第二种方法,利用递归
class Solution {
public:
ListNode* reverse(ListNode* pre,ListNode* cur){
if(cur == NULL)
return pre;
ListNode* temp = cur->next;
cur->next = pre;
return reverse(cur,temp);
}
ListNode* reverseList(ListNode* head) {
return reverse(NULL, head);
}
};
第二种递归不太好理解,
class Solution {
public:
ListNode* reverseList(ListNode* head) {
if(head == NULL)
return NULL;
if (head->next == NULL)
return head;
// 递归调用,翻转第二个节点开始往后的链表
ListNode *last = reverseList(head->next);
// 翻转头节点与第二个节点的指向
head->next->next = head;
// 此时的 head 节点为尾节点,next 需要指向 NULL
head->next = NULL;
return last;
}
};