双向链表:
双向链表也叫双链表,是链表的一种,它的每个数据 结点 中都有两个 指针 ,分别指向直接后继和直接前驱。 所以,从双向链表中的任意一个结点开始,都可以很方便地访问它的前驱结点和后继结点。
一,结构体定义及宏
#include <stdio.h>
#include <malloc.h>
/**
* 定义字符双向链表。
*/
typedef struct DoubleLinkNode{
char data;
struct DoubleLinkNode *previous;
struct DoubleLinkNode *next;
} DLNode, *DLNodePtr;
二,初始化双向链表
/**
* 初始化头节点。
* @return 返回头节点的指针。
*/
DLNodePtr initLinkList(){
DLNodePtr tempHeader = (DLNodePtr)malloc(sizeof(struct DoubleLinkNode));
tempHeader->data = '\0';
tempHeader->previous = NULL;
tempHeader->next = NULL;
return tempHeader;
}
三,打印双向链表
/**
* 打印链表。
* @param paraHeader 是链表的头结点。
*/
void printList(DLNodePtr paraHeader){
DLNodePtr p = paraHeader->next;
if(p == NULL){
printf("The list is empty.");
}
while(p != NULL){
printf("%c", p->data);
p = p->next;
}
printf("\r\n");
}
四,在给定的位置插入元素
/**
* 在给定的位置加入元素。
* @param paraHeader 是链表的头结点。
* @param paraChar 给定的char元素。
* @param paraPosition 给定的位置。
*/
void insertElement(DLNodePtr paraHeader, char paraChar, int paraPosition){
DLNodePtr p, q, r;
// Step 1. 搜索给定的位置。
p = paraHeader;
for(int i = 0; i < paraPosition; i ++){
p = p->next;
if(p == NULL){
printf("The position %d is beyond the scope of the list.", paraPosition);
return;
}
}
// Step 2. 创建一个新节点
q = (DLNodePtr)malloc(sizeof(struct DoubleLinkNode));
q->data = paraChar;
// Step 3. 链接链表和节点。
printf("linking\r\n");
r = p->next;
q->next = p->next;
q->previous = p;
p->next = q;
if (r != NULL){
r->previous = q;
}
}
五,删除指定元素
/**
* 删除链表中的元素。
* @param paraHeader 是链表的头结点。
* @param paraChar 给定的char元素。
*/
void deleteElement(DLNodePtr paraHeader, char paraChar){
DLNodePtr p, q, r;
p = paraHeader;
// Step 1. 定位
while((p->next != NULL) && (p->next->data != paraChar)){
p = p->next;
}
// Step 2. 检查错误
if(p->next == NULL){
printf("The char %c does not exist.\r\n", paraChar);
return;
}
// Step 3. 改变链接
q = p->next;
r = q->next;
p->next = r;
if(r != NULL){
r->previous = p;
}
// Step 4. 释放内存
free(q);
}
六,找到双向链表中特定数据的位置
/**
* 找到双向链表中特定的数据的位置
*/
void locateElement(DLNodePtr paraHeader, char paraChar){
DLNodePtr p;
p = paraHeader;
while(p->next != NULL){
p = p->next;
if(p->data == paraChar){
printf("Found the char %c\r\n", paraChar);
return;
}
}
printf("The char %c is not been found.\r\n", paraChar);
return ;
}
七,删除双向链表
void LinkDestory(DLNodePtr paraHeader){
DLNodePtr p;
p = paraHeader;
p->next = NULL;
printf("The list has been destoryed.\r\n");
return;
}
八,函数功能测试
/**
* 功能测试。
*/
void insertDeleteTest(){
// Step 1. 初始化一个空链表。
DLNodePtr tempList = initLinkList();
printList(tempList);
// Step 2. 加入一些字符。
insertElement(tempList, 'H', 0);
printList(tempList);
insertElement(tempList, 'e', 1);
printList(tempList);
insertElement(tempList, 'l', 2);
printList(tempList);
insertElement(tempList, 'l', 3);
printList(tempList);
insertElement(tempList, 'o', 4);
printList(tempList);
insertElement(tempList, '!', 5);
printList(tempList);
// Step 3. 删除一些字符(第一次出现的)。
deleteElement(tempList, 'e');
printList(tempList);
deleteElement(tempList, 'a');
printList(tempList);
deleteElement(tempList, 'o');
printList(tempList);
// Step 4. 在链表给定位置插入特定元素。
insertElement(tempList, 'o', 1);//首位插入
printList(tempList);
insertElement(tempList, 'o', 5);//末位插入
printList(tempList);
insertElement(tempList, 'o', 3);//中间插入
printList(tempList);
// Step 5. 查找指定元素
locateElement(tempList, 'H');
locateElement(tempList, 'l');
locateElement(tempList, 'o');
locateElement(tempList, 'a');
// Step 6. 清空链表
LinkDestory(tempList);
printList(tempList);
}
在老师的基础上,加入了对插入功能的更进一步的测试,同时加入了更多的打印函数来更好地了解函数地运行规律,之后又加入了对locateElement函数地测试。
九,对于链表地址地测试
/**
* 地址测试.
*/
void basicAddressTest(){
DLNode tempNode1, tempNode2;
tempNode1.data = 4;
tempNode1.next = NULL;
tempNode2.data = 6;
tempNode2.next = NULL;
printf("The first node: %d, %d, %d\r\n",
&tempNode1, &tempNode1.data, &tempNode1.next);
printf("The second node: %d, %d, %d\r\n",
&tempNode2, &tempNode2.data, &tempNode2.next);
tempNode1.next = &tempNode2;
}
十,程序入口
/**
* 程序入口。
*/
int main(){
insertDeleteTest();
basicAddressTest();
}
十一,运行结果
The list is empty.
linking
H
linking
He
linking
Hel
linking
Hell
linking
Hello
linking
Hello!
Hllo!
The char a does not exist.
Hllo!
Hll!
linking
Holl!
linking
Holl!o
linking
Holol!o
Found the char H
Found the char l
Found the char o
The char a is not been found.
The list has been destoryed.
The list is empty.
The first node: 6421968, 6421968, 6421984
The second node: 6421936, 6421936, 6421952
静态链表
静态链表,也是线性存储结构的一种,它兼顾了顺序表和链表的优点于一身,可以看做是顺序表和链表的升级版。
使用静态链表存储数据,数据全部存储在数组中(和顺序表一样),但存储位置是随机的,数据之间"一对一"的逻辑关系通过一个整形变量(称为"游标",和指针功能类似)维持(和链表类似)。
例如,使用静态链表存储 {1,2,3}
的过程如下:
创建一个足够大的数组,假设大小为 6,如图一所示:
图 一 空数组
接着,在将数据存放到数组中时,给各个数据元素配备一个整形变量,此变量用于指明各个元素的直接后继元素所在数组中的位置下标,如图 2 所示:
图 二 静态链表存储数据
通常,静态链表会将第一个数据元素放到数组下标为 1 的位置(a[1])中。
图 2 中,从 a[1] 存储的数据元素 1 开始,通过存储的游标变量 3,就可以在 a[3] 中找到元素 1 的直接后继元素 2;同样,通过元素 a[3] 存储的游标变量 5,可以在 a[5] 中找到元素 2 的直接后继元素 3,这样的循环过程直到某元素的游标变量为 0 截止(因为 a[0] 默认不存储数据元素)。
类似图 2 这样,通过 "数组+游标" 的方式存储具有线性关系数据的存储结构就是静态链表。
一,结构体定义及宏
#include <stdio.h>
#include <malloc.h>
#define DEFAULT_SIZE 5
typedef struct StaticLinkedNode{
char data;
int next;
} *NodePtr;
typedef struct StaticLinkedList{
NodePtr nodes;
int* used;
} *ListPtr;
二,初始化静态链表
/**
* 初始化头节点。
* @return 返回头节点的指针。
*/
ListPtr initLinkedList(){
// 指向整个链表空间的指针
ListPtr tempPtr = (ListPtr)malloc(sizeof(struct StaticLinkedList));
// 分配总空间
tempPtr->nodes = (NodePtr)malloc(sizeof(struct StaticLinkedNode) * DEFAULT_SIZE);
tempPtr->used = (int*)malloc(sizeof(int) * DEFAULT_SIZE);
// 第一个节点是头结点
tempPtr->nodes[0].data = '\0';
tempPtr->nodes[0].next = -1;
// 初始化时只有头结点使用
tempPtr->used[0] = 1;
for (int i = 1; i < DEFAULT_SIZE; i ++){
tempPtr->used[i] = 0;
}
return tempPtr;
}
三,打印静态链表
/**
* 打印链表。
* @param paraHeader 是链表的头结点。
*/
void printList(ListPtr paraListPtr){
int p = 0;
while (p != -1) {
printf("%c", paraListPtr->nodes[p].data);
p = paraListPtr->nodes[p].next;
}
printf("\r\n");
}
四,在给定的位置插入元素
/**
* 在给定的位置加入元素。
* @param paraHeader 是链表的头结点。
* @param paraChar 给定的char元素。
* @param paraPosition 给定的位置。
*/
void insertElement(ListPtr paraListPtr, char paraChar, int paraPosition){
int p, q, i;
// Step 1. 搜索位置
p = 0;
for (i = 0; i < paraPosition; i ++) {
p = paraListPtr->nodes[p].next;
if (p == -1) {
printf("The position %d is beyond the scope of the list.\r\n", paraPosition);
return;
}
}
// Step 2. 创建一个新的节点
for ( i; i < DEFAULT_SIZE; i ++){
if (paraListPtr->used[i] == 0){
printf("Space at %d allocated.\r\n", i);
paraListPtr->used[i] = 1;
q = i;
break;
}
}
if (i >= DEFAULT_SIZE){
printf("No space.\r\n");
return;
}
paraListPtr->nodes[q].data = paraChar;
// Step 3. 链接节点
printf("linking\r\n");
paraListPtr->nodes[q].next = paraListPtr->nodes[p].next;
paraListPtr->nodes[p].next = q;
}
五,删除指定元素
/**
* 删除链表中的元素。
* @param paraHeader 是链表的头结点。
* @param paraChar 给定的char元素。
*/
void deleteElement(ListPtr paraListPtr, char paraChar){
int p, q;
p = 0;
while ((paraListPtr->nodes[p].next != -1) && (paraListPtr->nodes[paraListPtr->nodes[p].next].data != paraChar)){
p = paraListPtr->nodes[p].next;
}
if (paraListPtr->nodes[p].next == -1) {
printf("Cannot delete %c\r\n", paraChar);
return;
}
q = paraListPtr->nodes[p].next;
paraListPtr->nodes[p].next = paraListPtr->nodes[paraListPtr->nodes[p].next].next;
// 此语句和free(q)功能相同
paraListPtr->used[q] = 0;
}
六,函数功能测试
/**
* 功能测试。
*/
void appendInsertDeleteTest(){
// Step 1. 初始化一个空链表
ListPtr tempList = initLinkedList();
printList(tempList);
// Step 2. 加入一些元素
insertElement(tempList, 'H', 0);
insertElement(tempList, 'e', 1);
insertElement(tempList, 'l', 2);
insertElement(tempList, 'l', 1);
insertElement(tempList, 'l', 3);
insertElement(tempList, 'o', 4);
printList(tempList);
// Step 3. 删除一些元素(首次出现的该元素)
printf("Deleting 'e'.\r\n");
deleteElement(tempList, 'e');
printf("Deleting 'a'.\r\n");
deleteElement(tempList, 'a');
printf("Deleting 'o'.\r\n");
deleteElement(tempList, 'o');
printList(tempList);
insertElement(tempList, 'x', 1);
printList(tempList);
}
增加了部分的打印函数以更好地理解函数功能。
七,程序入口
/**
* 程序入口
*/
void main(){
appendInsertDeleteTest();
}
八,运行结果
Space at 1 allocated.
linking
Space at 2 allocated.
linking
Space at 3 allocated.
linking
Space at 4 allocated.
linking
No space.
No space.
Hlel
Deleting 'e'.
Deleting 'a'.
Cannot delete a
Deleting 'o'.
Cannot delete o
Hll
Space at 2 allocated.
linking
Hxll
完整代码
#include <stdio.h>
#include <malloc.h>
/**
* 定义字符双向链表。
*/
typedef struct DoubleLinkNode{
char data;
struct DoubleLinkNode *previous;
struct DoubleLinkNode *next;
} DLNode, *DLNodePtr;
/**
* 初始化头节点。
* @return 返回头节点的指针。
*/
DLNodePtr initLinkList(){
DLNodePtr tempHeader = (DLNodePtr)malloc(sizeof(struct DoubleLinkNode));
tempHeader->data = '\0';
tempHeader->previous = NULL;
tempHeader->next = NULL;
return tempHeader;
}
/**
* 打印链表。
* @param paraHeader 是链表的头结点。
*/
void printList(DLNodePtr paraHeader){
DLNodePtr p = paraHeader->next;
if(p == NULL){
printf("The list is empty.");
}
while(p != NULL){
printf("%c", p->data);
p = p->next;
}
printf("\r\n");
}
/**
* 在给定的位置加入元素。
* @param paraHeader 是链表的头结点。
* @param paraChar 给定的char元素。
* @param paraPosition 给定的位置。
*/
void insertElement(DLNodePtr paraHeader, char paraChar, int paraPosition){
DLNodePtr p, q, r;
// Step 1. 搜索给定的位置。
p = paraHeader;
for(int i = 0; i < paraPosition; i ++){
p = p->next;
if(p == NULL){
printf("The position %d is beyond the scope of the list.", paraPosition);
return;
}
}
// Step 2. 创建一个新节点
q = (DLNodePtr)malloc(sizeof(struct DoubleLinkNode));
q->data = paraChar;
// Step 3. 链接链表和节点。
printf("linking\r\n");
r = p->next;
q->next = p->next;
q->previous = p;
p->next = q;
if (r != NULL){
r->previous = q;
}
}
/**
* 删除链表中的元素。
* @param paraHeader 是链表的头结点。
* @param paraChar 给定的char元素。
*/
void deleteElement(DLNodePtr paraHeader, char paraChar){
DLNodePtr p, q, r;
p = paraHeader;
// Step 1. 定位
while((p->next != NULL) && (p->next->data != paraChar)){
p = p->next;
}
// Step 2. 检查错误
if(p->next == NULL){
printf("The char %c does not exist.\r\n", paraChar);
return;
}
// Step 3. 改变链接
q = p->next;
r = q->next;
p->next = r;
if(r != NULL){
r->previous = p;
}
// Step 4. 释放内存
free(q);
}
/**
* 找到双向链表中特定的数据的位置
*/
void locateElement(DLNodePtr paraHeader, char paraChar){
DLNodePtr p;
p = paraHeader;
while(p->next != NULL){
p = p->next;
if(p->data == paraChar){
printf("Found the char %c\r\n", paraChar);
return;
}
}
printf("The char %c is not been found.\r\n", paraChar);
return ;
}
void LinkDestory(DLNodePtr paraHeader){
DLNodePtr p;
p = paraHeader;
p->next = NULL;
printf("The list has been destoryed.\r\n");
return;
}
/**
* 功能测试。
*/
void insertDeleteTest(){
// Step 1. 初始化一个空链表。
DLNodePtr tempList = initLinkList();
printList(tempList);
// Step 2. 加入一些字符。
insertElement(tempList, 'H', 0);
printList(tempList);
insertElement(tempList, 'e', 1);
printList(tempList);
insertElement(tempList, 'l', 2);
printList(tempList);
insertElement(tempList, 'l', 3);
printList(tempList);
insertElement(tempList, 'o', 4);
printList(tempList);
insertElement(tempList, '!', 5);
printList(tempList);
// Step 3. 删除一些字符(第一次出现的)。
deleteElement(tempList, 'e');
printList(tempList);
deleteElement(tempList, 'a');
printList(tempList);
deleteElement(tempList, 'o');
printList(tempList);
// Step 4. 在链表给定位置插入特定元素。
insertElement(tempList, 'o', 1);//首位插入
printList(tempList);
insertElement(tempList, 'o', 5);//末位插入
printList(tempList);
insertElement(tempList, 'o', 3);//中间插入
printList(tempList);
// Step 5. 查找指定元素
locateElement(tempList, 'H');
locateElement(tempList, 'l');
locateElement(tempList, 'o');
locateElement(tempList, 'a');
// Step 6. 清空链表
LinkDestory(tempList);
printList(tempList);
}
/**
* 地址测试.
*/
void basicAddressTest(){
DLNode tempNode1, tempNode2;
tempNode1.data = 4;
tempNode1.next = NULL;
tempNode2.data = 6;
tempNode2.next = NULL;
printf("The first node: %d, %d, %d\r\n",
&tempNode1, &tempNode1.data, &tempNode1.next);
printf("The second node: %d, %d, %d\r\n",
&tempNode2, &tempNode2.data, &tempNode2.next);
tempNode1.next = &tempNode2;
}
/**
* 程序入口。
*/
int main(){
insertDeleteTest();
basicAddressTest();
}
#include <stdio.h>
#include <malloc.h>
#define DEFAULT_SIZE 5
typedef struct StaticLinkedNode{
char data;
int next;
} *NodePtr;
typedef struct StaticLinkedList{
NodePtr nodes;
int* used;
} *ListPtr;
/**
* 初始化头节点。
* @return 返回头节点的指针。
*/
ListPtr initLinkedList(){
// 指向整个链表空间的指针
ListPtr tempPtr = (ListPtr)malloc(sizeof(struct StaticLinkedList));
// 分配总空间
tempPtr->nodes = (NodePtr)malloc(sizeof(struct StaticLinkedNode) * DEFAULT_SIZE);
tempPtr->used = (int*)malloc(sizeof(int) * DEFAULT_SIZE);
// 第一个节点是头结点
tempPtr->nodes[0].data = '\0';
tempPtr->nodes[0].next = -1;
// 初始化时只有头结点使用
tempPtr->used[0] = 1;
for (int i = 1; i < DEFAULT_SIZE; i ++){
tempPtr->used[i] = 0;
}
return tempPtr;
}
/**
* 打印链表。
* @param paraHeader 是链表的头结点。
*/
void printList(ListPtr paraListPtr){
int p = 0;
while (p != -1) {
printf("%c", paraListPtr->nodes[p].data);
p = paraListPtr->nodes[p].next;
}
printf("\r\n");
}
/**
* 在给定的位置加入元素。
* @param paraHeader 是链表的头结点。
* @param paraChar 给定的char元素。
* @param paraPosition 给定的位置。
*/
void insertElement(ListPtr paraListPtr, char paraChar, int paraPosition){
int p, q, i;
// Step 1. 搜索位置
p = 0;
for (i = 0; i < paraPosition; i ++) {
p = paraListPtr->nodes[p].next;
if (p == -1) {
printf("The position %d is beyond the scope of the list.\r\n", paraPosition);
return;
}
}
// Step 2. 创建一个新的节点
for ( i; i < DEFAULT_SIZE; i ++){
if (paraListPtr->used[i] == 0){
printf("Space at %d allocated.\r\n", i);
paraListPtr->used[i] = 1;
q = i;
break;
}
}
if (i >= DEFAULT_SIZE){
printf("No space.\r\n");
return;
}
paraListPtr->nodes[q].data = paraChar;
// Step 3. 链接节点
printf("linking\r\n");
paraListPtr->nodes[q].next = paraListPtr->nodes[p].next;
paraListPtr->nodes[p].next = q;
}
/**
* 删除链表中的元素。
* @param paraHeader 是链表的头结点。
* @param paraChar 给定的char元素。
*/
void deleteElement(ListPtr paraListPtr, char paraChar){
int p, q;
p = 0;
while ((paraListPtr->nodes[p].next != -1) && (paraListPtr->nodes[paraListPtr->nodes[p].next].data != paraChar)){
p = paraListPtr->nodes[p].next;
}
if (paraListPtr->nodes[p].next == -1) {
printf("Cannot delete %c\r\n", paraChar);
return;
}
q = paraListPtr->nodes[p].next;
paraListPtr->nodes[p].next = paraListPtr->nodes[paraListPtr->nodes[p].next].next;
// 此语句和free(q)功能相同
paraListPtr->used[q] = 0;
}
/**
* 功能测试。
*/
void appendInsertDeleteTest(){
// Step 1. 初始化一个空链表
ListPtr tempList = initLinkedList();
printList(tempList);
// Step 2. 加入一些元素
insertElement(tempList, 'H', 0);
insertElement(tempList, 'e', 1);
insertElement(tempList, 'l', 2);
insertElement(tempList, 'l', 1);
insertElement(tempList, 'l', 3);
insertElement(tempList, 'o', 4);
printList(tempList);
// Step 3. 删除一些元素(首次出现的该元素)
printf("Deleting 'e'.\r\n");
deleteElement(tempList, 'e');
printf("Deleting 'a'.\r\n");
deleteElement(tempList, 'a');
printf("Deleting 'o'.\r\n");
deleteElement(tempList, 'o');
printList(tempList);
insertElement(tempList, 'x', 1);
printList(tempList);
}
/**
* 程序入口
*/
void main(){
appendInsertDeleteTest();
}