链表
链表是线性表的一种,是一种物理存储单元上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。
链表常见操作有:
函数 | 功能 |
---|---|
insert(node,index) | 将node插入到链表中下标为index的位置 |
output() | 输出整个链表 |
delete_node(index) | 将链表下标为index的元素删除 |
reverse() | 翻转整个链表 |
单向链表
在第一个结点(有效元素)之前附设一个结点,称之为头结点;指向头结点的指针,称之为头指针。
对单链表的存取必须从头指针
开始进行,由于单链表的最后一个元素没有直接后继,则指针为NULL。
对于头结点,数据域可以不存储任何信息,也可存储如链表长度等附加信息。
小结:
- 无论链表是否为空,头指针均不为空。头指针是链表的必要条件;
- 链表可以没有结点,但不能没有头指针,头指针是链表的必要条。
//定义结点的结构体
typedef struct Node{
int data;
struct Node *next;
}Node, *LinkList;
则定义LinkList L;
时,L为链表的头指针。
L=(LinkList)malloc(sizeof(Node)); //创建一个结点
此处返回给L的是一个指针,并且赋给了头指针。
L->next=null; //这里说明我创建了一个头结点,即同时运用了头指针和头结点。
示例如下:
#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);
current_node = current_node->next;
}
printf("\n");
}
LinkedList delete_node(LinkedList head, int index) {
if (head == NULL) {
printf("failed\n");
return head;
}
Node *current_node = head;
int count = 0;
if (index == 0) {
head = current_node -> next;
free(current_node);
printf("success\n");
return head;
}
while (current_node -> next != NULL && count < index - 1) {
current_node = current_node -> next;
count++;
}
if (count == index - 1 && current_node -> next != NULL) {
Node *delete_node = current_node -> next;
current_node -> next = delete_node -> next;
free(delete_node);
printf("success\n");
return head;
}
printf("failed\n");
return head;
}
LinkedList reverse(LinkedList head) {
if (head == NULL) {
return head;
}
Node *current_node, *next_node;
current_node = head -> next;
head -> next = NULL;
while (current_node != NULL) {
next_node = current_node -> next;
current_node -> next = head;
head = current_node;
current_node = next_node;
}
return head;
}
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 count, caseNum, a, b;
int i = 0;
scanf("%d", &count);
while (i++ < count) {
scanf("%d", &caseNum);
switch (caseNum) {
case 1 :
scanf("%d %d", &a, &b);
Node *node = (Node *)malloc(sizeof(Node));
node -> data = b;
node -> next = NULL;
linkedlist = insert(linkedlist, node, a);
break;
case 2 :
output(linkedlist);
break;
case 3 :
scanf("%d", &a);
linkedlist = delete_node(linkedlist, a);
break;
case 4 :
linkedlist = reverse(linkedlist);
break;
}
}
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;
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;
}
双向链表
双向链表又叫双链表,相对于单链表,双链表的指针域有两个指针,分别指向前驱结点和后继结点。有了这样的结构,则可以从头结点遍历到尾结点,也可以从尾结点遍历到头结点。