链表
概述:
是一种数据结构
单链表:
链表种节点是离散的在内存中开辟空间的 因为是离散开辟,内存地址通常不是连续的,地址不一定相邻,甚至可能存在其他数据在它们之间。
双链表
1 定义节点
分析:
一个节点就是一个结构体变量。
如:
单链表节点
typedef struct node {//数据域结构体成员变量;//int age ;.....//地址域Node *next; }Node;
双链表节点
但是Node别名 是在下面定义的 内部怎么使用呢
typedef struct node {//数据域结构体成员变量;//地址域 // Node *next; // 但是 Node别名 是在下面定义的 内部怎么使用呢 // Node *head; // 使用别名就会报错//地址域 //解决 使用全名 也就是结构体 加结构体名struct node *next;struct node *head; }Node;
2 组建链表节点
2.1 静态组建节点
#include <stdio.h>// 静态组建双链表节点typedef struct node {// 数据域int num;// 地址域struct node *next;struct node *head; } Node; int main(int argc, char const *argv[]) {Node n01 = {1, NULL, NULL}; // 节点1Node n02 = {2, NULL, NULL}; // 节点2Node n03 = {3, NULL, NULL}; // 节点3n01.next = &n02;n02.head = &n01; // 1 2 链表进行关联上了n02.next = &n03;n03.head = &n02; // 2 3 链表进行关联上了// 链表遍历void print_link(Node * head){// 将首节点 赋值给当前节点Node *n = head;// 判断当前节点是否为NULLwhile (n != NULL){// 当前节点不为空,打印其数据printf("%d\n", n->num);// 移动当前节点到下一个节点n = n->next;}}print_link(&n01);printf("==============\n");print_link(&n02);return 0; }
2.2 动态组建节点【*】
可以动态的操作 也就是局限性小
有序的 是指存的顺序 和取出顺序一样
就是怎么存的 怎么取的
比如 4 2 1 3 取出的时候 顺序也是 4 2 1 3
因为动态组建链表节点 是 使用 指针 所以
节点之间的链接方式,不应该使用取地址 & 操作,而应该直接使用节点指针。
> n01->next = &n02; 错误的 > n01->next = n02; 正确的
动态创建链表节点是和静态的创建节点是有区别的
#include <stdio.h> #include <stdlib.h> // 动态组建双链表节点 typedef struct node {// 数据域int num;// 地址域struct node *next;struct node *head; } Node; int main(int argc, char const *argv[]) {Node *n01 = (Node *)calloc(1, sizeof(Node)); // 节点1Node *n02 = (Node *)calloc(1, sizeof(Node)); // 节点2Node *n03 = (Node *)calloc(1, sizeof(Node)); // 节点3// 判空if (n01 == NULL || n02 == NULL || n03 == NULL){return 0;}// 首先 头节点置空n01->head = NULL; // 1 链表的头节点置空// 节点之间的链接方式,不应该使用取地址 & 操作,而应该直接使用节点指针。 n01->next = &n02; 错误的n01->next = n02; // 1 2 链表就关联上了n02->next = n03;n03->head = n02; // 2 3 链表就关联上了// 尾节点置空n03->next = NULL;n01->num = 1;n02->num = 2;n03->num = 3;// 链表遍历void print_link(Node * head){// 将首节点 赋值给当前节点Node *n = head;// 判断当前节点是否为NULLwhile (1){// 当前节点不为空,打印其数据printf("%d\n", n->num);// 移动当前节点到下一个节点if (n->next == NULL){break;}n = n->next;}}// 遍历链表 方式2 void print_link1(Node * head){Node *jiedian = head;while (jiedian != NULL) // 只要有 节点就进行遍历{// 打印节点数据printf("节点数据为 %d\n", jiedian->num);// 节点往后移动一个jiedian = jiedian->next;}}printf("=======\n"); print_link(n01); // 传递的参数应该是节点指针 p,而不是节点指针的地址 &p。print_link1(n01);printf("=======\n");print_link(n03);printf("=======\n");print_link(n02);// 手动释放内存 free(n01);free(n02);free(n03);return 0;}
3 链表的操作
3.1 添加:
目的: 链表尾部增加
所需条件: 1 给谁添加 (首节点 head)
2 要添加的节点 newNode
函数:
/* 参数:head : 链表的第一个位置 就是首节点newNode: 要添加的节点 返回值 :链表的首节点 */ // 链表的添加 Node *add_node(Node *head, Node *newNode) {if (head == NULL){head = newNode;return head;}Node *n = head; // n 为设置的 当前节点while (n->next != NULL){n = n->next; // 将当前节点移动到下一节点}// 当循环结束后n就是最后一个节点 【要是单链表就结束了】n->next = newNode;// 将新节点的上一个节点设置为原来的最后一个节点 【双链表结束】newNode->head = n;return head; }
优化:
/* 参数:head : 链表的第一个位置 就是首节点newNode: 要添加的节点 返回值 :链表的首节点 */ Node* add_node(Node *head,Node *newNode) { if(head == NULL) {head = newNode;return head; } Node *n = head; //n 为设置的 当前节点 while( n->next != NULL) {n = n->next; //将当前节点移动到下一节点 } //当循环结束后n就是最后一个节点 【要是单链表就结束了】n->next = newNode; //将新节点的上一个节点设置为原来的最后一个节点 【双链表结束】newNode->head = n;return head; }
3.2 遍历
/*参数:head :要遍历的链表的首节点 */ void print_link(Node *head) // 遍历节点 {// 将首节点 赋值给当前节点Node *n = head;// 判断当前节点是否为NULLwhile (n != NULL){// 当前节点不为空,打印其数据printf("%d\n", n->num);// 移动当前节点到下一个节点n = n->next;} }
3.3 删除
/* 参数:head: 头节点delNode:要删除的节点 返回值:删除后链表的首节点 作用:删除指定节点 */ Node *del_node(Node *head, Node *delNode) {// 判断是否有首节点if (head == NULL){printf("没有首节点\n");return NULL;}// 判断要删除的节点是不是空的if (delNode == NULL){printf("要删除的节点为NULL\n");return head;}// 当要删除的节点就是首节点if (head == delNode){// 要删除的节点为首节点// 将下一个节点设为首节点head = head->next;// 将 先在的首节点的头节点 置空head->head = NULL;// 释放原来的首节点free(delNode);return head;}// 要删除的节点不是首节点,此时寻找要删除的节点// n 当前节点Node *n = head;while (n != delNode && n != NULL){n = n->next;}if (n == NULL){printf("没有要删除的节点");}// 判断要删除的节点就是最后一个节点的时候if (n->next == NULL){Node *headNode = n->head;headNode->next = NULL;free(n);return head;}// n就是要删除的节点Node *headNode = n->head; // 取出n的上一个节点Node *nextNode = n->next; // 取出n的下 一个节点free(n); // 释放nheadNode->next = nextNode; // 让n的上一个节点的下一个节点为n的下一个节点nextNode->head = headNode; // 让n的下一个节点的下一个节点为n的上一个节点return head; }
3.4 修改
/* 参数:head:首节点 num:查找的值newNum:修改后的值 返回值: 无 作用: 按值 按位 修改指定节点的值
// 按值 修改 #include <stdio.h> void update_link(Node *head, int num, int newNum) {Node *n = find_link(head, num);if (n == NULL){return;}n->num = newNum; }
/* 作用:按位置修改指定节点的值 参数:head:首节点index:修改的位置newNum:修改后的值 返回值:无 */ void update_i(Node *head, int index, int newNum) {Node *n = find_i(head, index);if (n == NULL){return;}n->num = newNum; }
3.5 插入
目的:随意位置插入
/* 参数:head: 首节点index 要插入的节点位置newNode 新节点返回值:插入后首节点 作用:按位置插入新节点*/ // 思路 按你想要插入的位置插入 Node *insertlink(Node *head, int index, Node *newNode) {if (index == 0){newNdode->next = head;head->head = newNode;head = newNode;return head;}// index 位置原来的节点Node *n = find_index_link(head, index){Node *n = find_index_link(head, index);if (n == NULL){add_link(head, newNode);// add_link 是添加 链表节点 函数return head;}Node *headNode = n->head;headNode->next = newNode;newNode->head = headNode;newNode->next = n;n->head = newNode;return head;}}
获取链表长度
/* 参数:首节点 作用:获取链表长度 返回值:链表长度 */ int get_len(Node *head) {int i = 0;Node *n = head;while (n != NULL){i++;n = n->next;}return i; }
3.6 查找
按位置进行查找节点
- 1 按值查找
- 2 按位查找
/* 作用:按结构体中存储的值查找对应的节点 参数:head:首节点num:查找的值 返回值:查询到的节点,如果为NULL证明不存在 */
//【 1 按值查找 】 Node * find_link(Node * head,int num) {Ndoe *n =head;while(n != NULL){if(n-> num == num){return n;}n = n->next;}return NULL; }
// 【 2 按位查找 】 /* 作用: 按结构体在链表中的位置查找对应的节点 参数: head:首节点 index:下标 返回值: 查询到的节点,如果为NULL证明不存在 */ Node *find_i(Node *head, int index) {Node *n = head;int i = 0;while (n != NULL){if (i == index){return n;}n = n->next;i++;}return NULL; }
3.7 逆序
#include <stdio.h> // 逆序排列 void print_link_r(Node *head) {int len = get_len(head);int index = len - 1; // 下标就是长度减一 就是最后一位的下标Node *n = find_i(head, index);while (n != NULL){printf("%d\n", n->num);n = n->head;} }
3.8 排序
顺序
Node *sort_link(Node *head)
{if (head == NULL){return NULL;}int len = get_len(head);for (int i = 0; i < len; i++){for (int j = 0; j < len - 1; j++){Node *n = find_i(head, j);Node *nn = n->next;if (n->num > nn->num){if (j == 0){Node *nnn = nn->next;nn->next = n;n->head = nn;nn->head = NULL;n->next = nnn;nnn->head = n;head = nn;}else if (j == len - 2){Node *hn = n->head; // 倒数第三个节点hn->next = nn;nn->head = hn;nn->next = n;n->head = nn;n->next = NULL;}else{Node *hn = n->head;Node *nnn = nn->next;hn->next = nn;nn->head = hn;nn->next = n;n->head = nn;n->next = nnn;nnn->head = n;}}}}return head;
}
逆序排列
//逆序排列
void print_link_desc(Node *head)
{int len = get_len(head);int index =len-1 ; // 下标就是长度减一 就是最后一位的下标Node *n = find_i(head,index);while (n != NULL){printf("%d\n",n->num);n = n->head;}
}
3.9 释放
#include <stdio.h>
void free_link(Node *head)
{Node *n = head;while (n != NULL){Node *nextNode = n->next;free(n);n = nextNode;}
}