目录
链表简介:
C语言中的链表是一种常见的数据结构,它由一系列的节点(node)组成,每个节点包含一个数据元素和一个指向下一个节点的指针。链表可以用来表示各种数据结构,例如队列、堆栈、树等。
在C语言中,链表的节点通常是通过结构体来定义的。一个简单的链表节点结构体的定义如下:
struct Node {
int data;
struct Node* next;
};
在这个结构体中,data
是节点存储的数据元素,next
是指向下一个节点的指针。因为next
指向的是另一个Node
类型的结构体,所以需要使用struct Node*
类型来定义。
链表的头指针是指向第一个节点的指针,通常称为head
。对于一个空链表,头指针为NULL
。链表的尾节点的next
指针指向NULL
,表示链表的末尾。
链表的优点在于它可以动态地增加或删除元素,而不需要像数组那样需要预留空间。然而,由于链表中的元素是不连续存储的,访问链表中的某个元素需要遍历整个链表,因此在访问链表中的元素时,时间复杂度为O(n),其中n是链表中的元素个数。
以下是用c语言写的链表:
/*
This shows the coding basics of single-linked-list in C programming.
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdbool.h>
struct node
{
int data;
int key;
struct node *next;
};
struct node *head = NULL;
struct node *current = NULL;
//display the list
void printList()
{
// reset to head
struct node *ptr = head;
//printf("\nhead address: %p\n", head);
printf("\n[ ");
//start from the beginning
while(ptr != NULL)
{
printf("(%d,%d,%p) ",ptr->key,ptr->data,ptr->next);
//printf("(%d,%d) ",ptr->key,ptr->data);
ptr = ptr->next;
}
//end at the last node
printf(" ]");
}
//insert link at the first location
void insertFirst(int key, int data)
{
//create a link
struct node *link = (struct node*) malloc(sizeof(struct node));
link->key = key;
link->data = data;
//point it to old first node
link->next = head;
//point first to new first node
head = link;
//printf("current head address: \n%p\n", head);
}
//delete first item
struct node* deleteFirst()
{
//save reference to first link
struct node *tempLink = head;
//mark next to first link as first
head = head->next;
//return the deleted link
return tempLink;
}
//is list empty
bool isEmpty()
{
return head == NULL;
}
int length()
{
int length = 0;
struct node *current;
for(current = head; current != NULL; current = current->next)
{
length++;
}
return length;
}
//find a link with given key
struct node* find(int key)
{
//start from the first link
struct node* current = head;
//if list is empty
if(head == NULL)
{
return NULL;
}
//navigate through list
while(current->key != key)
{
//if it is last node
if(current->next == NULL) {
return NULL;
} else {
//go to next link
current = current->next;
}
}
//if data found, return the current Link
return current;
}
//在中间插入,在key1后加入新节点
void insertBetween(int key1, int key, int data)
{
//create a link
struct node *link = (struct node*) malloc(sizeof(struct node));
struct node *next = NULL;
current = find(key1);
link->key = key;
link->data = data;
next = current->next;
current->next = link;
link->next = next;
}
//在中间删除,并且返回被删除的节点的地址
struct node*deletebetween(int key)
{
struct node* current = find(key);
struct node* next = NULL;
if(current->next == NULL)
{
return NULL;
}
next = current->next;
current->data = next->data;
current->key = next->key;
current->next = next->next;
free(next);
return current;
}
//delete a link with given key
struct node* delete(int key)
{
//start from the first link
struct node* current = head;
struct node* previous = NULL;
//if list is empty
if(head == NULL)
{
return NULL;
}
//navigate through list
while(current->key != key)
{
//if it is last node
if(current->next == NULL) {
return NULL;
} else {
//store reference to current link
previous = current;
//move to next link
current = current->next;
}
}
//found a match, update the link
if(current == head)
{
//change first to point to next link
head = head->next;
} else {
//bypass the current link
previous->next = current->next;
}
return current;
}
void sort()
{
int i, j, k, tempKey, tempData;
struct node *current;
struct node *next;
int size = length();
k = size ;
for ( i = 0 ; i < size - 1 ; i++, k-- )
{
current = head;
next = head->next;
for ( j = 1 ; j < k ; j++ )
{
if ( current->data > next->data )
{
// switch data
tempData = current->data;
current->data = next->data;
next->data = tempData;
// switch key
tempKey = current->key;
current->key = next->key;
next->key = tempKey;
}
current = current->next;
next = next->next;
}
}
}
void reverse(struct node** head_ref)
{
struct node* prev = NULL;
struct node* current = *head_ref;
struct node* next;
while (current != NULL)
{
// switch address
next = current->next;
current->next = prev;
prev = current;
current = next;
}
*head_ref = prev;
}
void main()
{
// insert node from the beginning
insertFirst(1,10);
insertFirst(2,20);
insertFirst(3,30);
insertFirst(4,40);
insertFirst(5,50);
insertFirst(6,60);
//在中间插入
insertBetween(2,2532,23452);
insertBetween(5,1324,54363);
//在中间删除
deletebetween(6);
deletebetween(2);
printf("Original List: ");
//print list
printList();
while(!isEmpty()) {
struct node *temp = deleteFirst();
printf("\nDeleted value:");
printf("(%d,%d) ",temp->key,temp->data);
}
printf("\nList after deleting all items: ");
printList();
// insert node from the beginning
insertFirst(7,70);
insertFirst(8,80);
insertFirst(9,90);
insertFirst(10,100);
insertFirst(11,110);
insertFirst(12,120);
printf("\nRestored List: ");
printList();
printf("\n");
struct node *foundLink = find(10);
if(foundLink != NULL) {
printf("Element found: ");
printf("(%d,%d) ",foundLink->key,foundLink->data);
printf("\n");
} else {
printf("Element not found.");
}
delete(10);
printf("List after deleting an item: ");
printList();
printf("\n");
foundLink = find(4);
if(foundLink != NULL) {
printf("Element found: ");
printf("(%d,%d) ",foundLink->key,foundLink->data);
printf("\n");
} else {
printf("Element not found.");
}
printf("\n");
sort();
printf("List after sorting the data: ");
printList();
reverse(&head);
printf("\nList after reversing the data: ");
printList();
printf("\nSingle linked list programmed......");
}
这是一个使用C语言编写的单向链表(single-linked-list)的基础代码。在这个程序中,定义了一个包含三个元素的结构体(int data,int key,struct node* next),用于表示单向链表的每个节点。其中,data表示该节点所携带的数据,key表示该节点的键值,next表示下一个节点的地址。
在代码中,定义了一个指向链表头部的指针head和一个指向当前节点的指针current,它们用于存储链表中的节点信息。
该程序提供了以下几个功能:
- display the list: 显示整个链表
- insert link at the first location: 在链表的头部插入一个节点
- delete first item: 删除链表中的第一个节点
- is list empty: 判断链表是否为空
- length: 返回链表的长度
- find a link with given key: 在链表中查找一个特定键值的节点
- insertBetween: 在链表中某个节点后插入一个新节点
- deletebetween: 删除链表中某个节点后的节点
- delete a link with given key: 删除链表中一个特定键值的节点
- sort: 对链表中的元素进行排序
- reverse: 反转链表中的元素
其中,insertBetween、deletebetween、sort、reverse等功能可以通过调用find函数在链表中查找节点来实现。在find函数中,通过遍历链表的方式找到对应的节点,如果没有找到则返回NULL。
下面是各个函数的详细解释:
printlist()函数:
这段代码是一个打印链表的函数,它会遍历链表并输出每个节点的键(key)、数据(data)和指向下一个节点的指针(next)。
首先,函数通过将指针ptr初始化为头(head)节点的指针来将ptr指向链表的开头。
然后,while循环遍历链表,每次迭代将ptr指向下一个节点,直到ptr指向NULL,表示已经到达链表的末尾。
在循环中,printf函数输出当前节点的键、数据和指针,然后将ptr指向下一个节点。
最后,函数在链表的末尾输出右方括号,表示已经完成打印操作。
insertFirst()函数:
这是一个将新节点插入到链表头部的函数。它首先创建一个新的节点(使用malloc()函数分配内存),然后将新节点的key和data设置为输入参数key和data的值。接下来,将新节点的next指针设置为指向链表的原始头部(head),这样它就成为了链表的新头部。最后,将head指针设置为指向新节点,以便新节点成为链表的新头部。
deleteFirst()函数:
这段代码定义了一个函数 deleteFirst()
,它返回一个指向 struct node
类型的指针。
函数的功能是删除链表的第一个节点,并返回一个指向该节点的指针。
首先,它保存了指向第一个节点的指针 head
到一个临时变量 tempLink
中。
然后将 head
指向链表的下一个节点,这样链表的第一个节点就被删除了。
最后,返回被删除节点的指针 tempLink
,以便进一步操作或释放内存。
isEmpty()函数:
这段代码定义了一个名为 isEmpty()
的函数,该函数返回一个 bool
类型值。函数内部的实现逻辑是检查链表是否为空。当链表为空时,即链表的头节点指针 head
为 NULL
,函数返回 true
,否则返回 false
。
这个函数通常用于在对链表进行操作时检查其是否为空。例如,可以在向链表中添加、删除元素之前使用此函数来确保链表中至少有一个节点。如果链表为空,则无法执行删除操作,因为没有可供删除的节点。
length()函数:
这段代码是定义了一个 length
函数,用于返回链表中节点的数量。该函数遍历整个链表,从头节点开始,依次遍历每个节点,并通过每次自增的计数器来计算链表的长度,直到遍历到最后一个节点。
具体而言,这段代码首先定义了一个整型变量 length
,用于记录链表长度,初始化为 0。然后定义了一个指向节点的指针变量 current
,将其初始化为头节点 head
。接着使用 for
循环遍历链表,每次循环将 current
指向下一个节点,同时将计数器 length
加 1。循环继续直到 current
指向 NULL,即到达链表末尾,最后返回计数器 length
,即为链表的长度。
find()函数:
这段代码是一个函数,它的作用是在链表中查找具有指定键的节点,并返回该节点的指针。函数接收一个整数键作为参数,表示要查找的节点的键值。
该函数的实现使用了一个while循环,首先将current指向链表的头节点head。然后在while循环中,循环条件是当前节点current的键值不等于要查找的键值。在每次循环中,如果当前节点是最后一个节点,则返回NULL表示未找到;否则继续将current指向下一个节点。最终如果找到了具有指定键值的节点,就返回该节点的指针。
insertBetween()函数:
这段代码定义了一个名为insertBetween
的函数,它接受三个整数参数:key1
、key
和data
。它的作用是在给定键值key1
的节点之后插入一个新的节点,新节点的键值为key
,数据为data
。
函数首先通过调用find
函数找到键值为key1
的节点。然后,它创建一个新节点,将新节点的键值和数据设置为传递给函数的值。接下来,它将当前节点的下一个节点保存在一个变量next
中。然后,它将新节点插入到当前节点后面,将新节点的下一个节点设置为之前保存的next
变量。最后,函数返回并终止。
deletebetween()函数:
这段代码实现了从链表中删除指定节点的功能。具体来说:
首先,使用 find()
函数查找指定关键字 key
的节点,并将该节点的指针保存在 current
变量中。
然后,检查该节点是否为链表的最后一个节点,如果是,则无法删除,直接返回 NULL
。
否则,将 next
变量指向下一个节点,并将下一个节点的数据和关键字复制到当前节点中。
接着,将当前节点的指针指向下一个节点的下一个节点,即删除下一个节点。同时,使用 free()
函数释放下一个节点的内存。
最后,返回指向当前节点的指针,表示已经删除了该节点。
delete()函数:
这段代码实现了从链表中删除给定键值的节点。它首先从链表的第一个节点开始遍历,直到找到与给定键值相匹配的节点或遍历完整个链表。在遍历过程中,它维护了指向当前节点和前一个节点的指针,以便在找到匹配节点后,可以更新链表中相应的指针,从而实现删除操作。
如果找到的节点是链表中的第一个节点,那么它将更新链表的头指针以指向下一个节点,从而将第一个节点删除。否则,它将跳过当前节点并更新前一个节点的“next”指针,使其指向下一个节点,从而删除当前节点。
函数最终返回删除的节点,以便在需要时可以释放其内存。如果没有找到匹配的节点,则返回“NULL”。
sort()函数:
这段代码实现了一个基本的排序算法,使用了冒泡排序(bubble sort)的一种变种。该函数的作用是对链表中的节点按照节点中的数据进行升序排序。
函数使用两个嵌套的循环来实现排序。外层循环迭代链表中的所有节点,内层循环比较相邻的节点,将它们按升序排序。排序的过程中,对于每一对需要交换的节点,该函数交换它们节点中的数据和键值(key),而不是直接交换节点本身,这是因为直接交换节点的话,还需要调整节点之间的链接关系。
该函数最终返回一个已排序的链表。由于链表中的节点数量可能很多,所以该函数的时间复杂度是O(n^2)级别的,不适合处理大型数据集。
reverse()函数:
这是一个反转单链表的函数。参数 head_ref
是指向指针的指针,指向链表头节点的地址。函数中使用了三个指针变量 prev
、current
和 next
,prev
初始化为 NULL
,current
初始为 *head_ref
,next
用于保存下一个节点的地址。接下来,使用 while 循环,将 current
节点指向其前面的节点 prev
,然后更新 prev
和 current
的指针。最后,将 head_ref
指向反转后的链表头节点 prev
。这个函数会改变原始链表,使其成为反转后的链表。
在链表的学习中有一些要注意的难点和易错点,比如说指针的使用,链表中的结点是通过指针连接起来的,因此指针的使用非常重要。初学者容易犯错的地方是没有及时更新指针或者没有正确地使用指针。还有内存管理,在链表中插入和删除结点时,需要动态地分配和释放内存。容易出错的地方是没有正确地分配或释放内存,导致内存泄漏或程序崩溃。还要注意边界条件,链表操作中需要考虑很多边界条件,例如链表为空或只有一个结点的情况,或者要在链表的头部或尾部进行操作时。如果没有考虑到这些边界条件,程序会出现错误。还有递归操作,链表的递归操作(例如递归反转链表)比较难理解,容易出现递归深度过大的问题,导致程序崩溃。还要注意性能问题,链表操作的时间复杂度与链表的长度相关,插入和删除操作的时间复杂度为O(1),但查找操作的时间复杂度为O(n)。因此,在实际应用中,需要权衡时间和空间的利弊,选择合适的数据结构。总之,学习链表需要仔细思考每个操作的含义和实现细节,一定要多练习和调试。