为什么要写这篇博客?
嵌入式开发中,在很多的环境下无法使用官方库,可以自己写数据结构,进行使用。
链表特点:
- 元素前后依赖
- 不能随机访问元素
- 易于删除和添加元素(但是需要内存管理机制)
方法:
- 构造
- 结点插入
- 遍历
- 结点删除
- 翻转
- 释放内存
单链表及结点构造
每个结点都有自己的数据内存data,下一个结点的地址用next指针存放。
Node名代表结点,*Linkedlist说明需要一个指针(存储地址)配合各种方法操作链表。
typedef struct Node{
int data;
struct Node *next;
}Node, *LinkedList;
结点插入(insert(结点指针,位置))
- 找到链表中要插入的位置
- 令待插入结点的next指针指向插入位置的当前结点
- 令待插入位置之前的next指向待插入结点
- 此时需要逐步遍历查找,所以时间复杂度为O(n)
LinkedList insert(LinkedList head, Node *node, int index) { if (head == NULL) { // 向空表的非0位置插入,肯定是错的 if (index != 0) { return head; } head = node; return head; } if (index == 0) { node->next = head; head = node; return head; } Node *current_node = head; int count = 0; // 这里需要根据index对插入位置前的链表进行定位 // 通过它的next对插入位置处的结点进行访问 while (current_node->next != NULL && count < index - 1) { current_node = current_node->next; count++; } if (count == index - 1) { // 先将插入位置结点地址赋给插入结点,否则前一个结点存放的地址就没了 node->next = current_node->next; current_node->next = node; } return head; }
遍历方法(output)
- 定义一个用于遍历的变量,初始指向头结点
- 输出遍历遍历所在结点的data,并更新遍历变量为当前结点的下一个结点
- 重复操作上一步,直到遍历完所有的结点
void output(LinkedList head) {
if (head == NULL) {
return;
}
Node *current_node = head;
while (current_node != NULL) {
printf("%d ", current_node->data);
current_node = current_node->next;
}
printf("\n");
}
删除结点(delete_node( index))
- 从头遍历找到要删除的位置
- 令删除位置前结点的next指向待删除结点后的结点
- 删除结点
一开始用一个指针连接head
然后用del代替cur,继续用head索引结点。
最后释放空间。
LinkedList delete_node(LinkedList head, int index) {
if (head == NULL) {
return head;
}
Node *current_node = head;
// 这里记得初始化为0
int count = 0;
if (index == 0) {
head = head->next;
free(current_node);
//这个也是一个出口
return head;
}
// 定位到指定结点的前一个。
while (current_node->next != NULL && count < index - 1) {
current_node = current_node->next;
count++;
}
// 这里是count == index - 1 不是count < index - 1
if (count == index - 1 && current_node->next != NULL) {
Node *delete_node = current_node->next;
current_node->next = delete_node->next;
free(delete_node);
}
return head;
}
链表翻转实现(reverse)
- 定义一个用于遍历的指针,初始指向头结点后的一个结点
- 让头结点的next指针置空
- 从当前遍历指针的结点开始遍历,将遍历到的结点next指向头结点,遍历过程借助另一个指针保存下一个遍历到的结点
- 重复上步骤直至表尾,此时新的链表就是原链表反转后的链表。
// 请在下面实现链表的反转函数 reverse
// 这个函数有疑问。
LinkedList reverse(LinkedList head){
if(head == NULL){
return head;
}
//遍历过程中下一个结点和当前结点
Node *next_node, *current_node;
// current_node指针,指向头结点后结点。
current_node = head->next;
head->next = NULL;
while(current_node != NULL ){
next_node = current_node->next;
// cur指向头结点
current_node->next = head;
head = current_node;
current_node = next_node;
}
return head;
}
释放内存(clear)
- 定义一个结点del存放传入的地址
- 传入地址next
- 释放del即可
void clear(LinkedList head) {
Node *current_node = head;
while (current_node != NULL) {
Node *delete_node = current_node;
current_node = current_node->next;
free(delete_node);
}
}
最后用一个简单程序测试函数
#include <stdio.h>
#include <stdlib.h>
typedef struct Node{
int data;
struct Node *next;
}Node, *LinkedList;
LinkedList insert(LinkedList head, Node *node, int index) {
if(head == NULL){
if(index != 0){
printf("failed\n");
return head;
}
head = node;
printf("success\n");
return head;
}
if(index == 0){
node->next = head;
head = node;
printf("success\n");
return head;
}
Node *current_node = head;
int count = 0;
while(current_node->next != NULL && count < index-1){
current_node = current_node->next;
count++;
}
if(count == index-1){
node->next = current_node->next;
current_node->next = node;
printf("success\n");
return head;
}
printf("failed\n");
return head;
}
void output(LinkedList head) {
if(head == NULL){
return ;
}
Node *current_node = head;
while(current_node != NULL){
printf("%d", current_node->data);
if(current_node->next != NULL){
printf(" ");
}
current_node = current_node->next;
}
printf("\n");
}
void clear(LinkedList head) {
Node *current_node = head;
while(current_node != NULL){
Node *delete_node = current_node;
current_node = current_node->next;
free(delete_node);
}
}
int main() {
LinkedList linkedlist = NULL;
int n,p,q;
scanf("%d", &n);
for (int i = 0; i < n; i++) {
scanf("%d%d", &p, &q);
Node *node = (Node*)malloc(sizeof(Node));
node->data = q;
node->next = NULL;
linkedlist = insert(linkedlist, node, p);
}
output(linkedlist);
clear(linkedlist);
return 0;
}
双向链表:
循环链表(约瑟夫环问题):
#include <stdio.h>
#include <stdlib.h>
typedef struct Node{
int data;
struct Node *next;
}Node, *LinkedList;
LinkedList insert(LinkedList head, Node *node, int index) {
if (head == NULL) {
if (index != 0) {
return head;
}
head = node;
head->next = head;
return head;
}
if (index == 0) {
node->next = head->next;
head->next = node;
//这里的head是尾结点
return head;
}
Node *current_node = head->next;
int count = 0;
while (current_node != head && count < index - 1) {
current_node = current_node->next;
count++;
}
if (count == index - 1) {
node->next = current_node->next;
current_node->next = node;
}
if(node == head->next){
head = node;
}
return head;
}
// 请在下面实现输出函数 output_josephus
void output_josephus(LinkedList head, int m){
Node *current_node = head;
head = NULL;
while(current_node->next != current_node){
for(int i = 1; i < m; i++){
current_node = current_node->next;
}
printf("%d ",current_node->next->data);
Node *delete_node = current_node->next;
current_node->next = current_node->next->next;
free(delete_node);
}
printf("%d\n",current_node->data);
free(current_node);
}
int main() {
LinkedList linkedlist = NULL;
int n, m;
scanf("%d %d", &n, &m);
for(int i = 1; i <= n; i++){
Node *node = (Node*)malloc(sizeof(Node));
node->data = i;
node->next = NULL;
linkedlist = insert(linkedlist, node, i-1);
}
output_josephus(linkedlist, m);
return 0;
}