数据结构——单链表

单链表的实现

一、数据的定义
1. 定义数据和常数
定义一个结构体
typedef struct city{
    int id;
    char * name;
    char * abbreviation;
}City, ElemType;
定义常量
#define TRUE 1
#define FALSE 0
#define NO_FOUND (-1)
#define NO_EXIST_ID (-999)
2. 定义结点和头结点

结点包括数据域指针域
头结点可以记录单链表的长度

typedef struct node{
    //数据域
    ElemType city;
    //指针域
    struct node * next;
}Node;

typedef struct linkedList{
    //头指针
    Node * head;
    //单链表的长度
    int length;
}LinkedList;
二、链表的基本功能
//operation
//初始化
void InitList(LinkedList * linkedList, ElemType * dataArray, int length);
//创建新结点
Node * newNode(ElemType * elemType);
//插入:头插、尾插和中间任意位置插入
void InsertElem(LinkedList * linkedList, int pos, ElemType elemType);
//删除:包括头删、尾删和中间任意位置删除
ElemType DeleteElem(LinkedList * linkedList, int pos);
//修改
int UpdateElem(LinkedList * linkedList, int pos, ElemType elemType);
//查找
//按位查找
ElemType * GetElemByPosition(LinkedList * linkedList, int pos);
//按值查找
int GetElemByValue(LinkedList * linkedList, ElemType * elemType);
//返回求前驱结点
ElemType GetPreElem(LinkedList * linkedList, ElemType * elemType);
//返回后继结点
ElemType GetNextElem(LinkedList * linkedList, ElemType * elemType);
//返回链表的长度
int GetLength(LinkedList * linkedList);
//判断链表是否为空
int IsEmpty(LinkedList * linkedList);
//清空链表
void ClearLinkedList(LinkedList * linkedList);
//打印链表
void printLinkedList(LinkedList * linkedList);
三、链表的实现
1. 初始化

长度length为插入结点的个数,在单链表中,描述结点的位置是从1开始的,因此在循环插入的过程中,使用i + 1

void InitList(LinkedList * linkedList, ElemType * dataArray, int length)
{
    linkedList->head = NULL;
    linkedList->length = 0;
    if(length < 0 || !dataArray){
        printf("初始化失败!\n");
        return;
    }
    for (int i = 0; i < length; ++i) {
        InsertElem(linkedList, i+1, dataArray[i]);
    }
}
2. 插入结点

在位置为pos处插入,需要找到第pos - 1个结点。在第一次循环时,指针已经移到了第二个位置,因此要找到第pos-1个结点,需要循环pos-2次。注意: 链表的length要加1。

void InsertElem(LinkedList * linkedList, int pos, ElemType elemType)
{
    /*Node * node = (Node *) malloc(sizeof(Node));
    node->next = NULL;
    node->city = elemType;*/
    Node * node = newNode(&elemType);
    Node * current = linkedList->head;
    //1. 头插
    if(pos == 1){
        //1.1 链表为空
        if(IsEmpty(linkedList) == TRUE){
            linkedList->head = node;
            linkedList->length++;
            return;
        }
        //1.2 链表不为空
        node->next = linkedList->head;
        linkedList->head = node;
        linkedList->length++;
        return;
    }
    //2. 中间位置插入包括在结尾插入
    for (int i = 1; current && i < pos - 1; ++i) {
        current = current->next;
    }
    if(!current){
        printf("插入失败!\n");
        return;
    }
    node->next = current->next;
    current->next = node;
    linkedList->length++;
}
3. 删除结点

注意:在返回删除结点的数据后,链表的长度要减1,并且要释放被删除结点的内存空间。

ElemType DeleteElem(LinkedList * linkedList, int pos)
{
    Node * current = linkedList->head;
    ElemType data;
    data.id = NO_EXIST_ID;
    //头删
    if(pos == 1){
        data = current->city;
        linkedList->head = current->next;
        free(current);
        linkedList->length--;
        return data;
    }
    //任意位置删除(包括尾删)
    for (int i = 1; current && i < (pos-1); ++i) {
        current = current->next;
    }
    if(!current){
        printf("删除失败!\n");
        return data;
    }
    Node * del_node = current->next;
    data = del_node->city;
    current->next = del_node->next;
    linkedList->length--;
    free(del_node);
    return data;
}
4. 修改结点
int UpdateElem(LinkedList * linkedList, int pos, ElemType elemType)
{
    Node * current = linkedList->head;
    if(!current){
        printf("链表为空!修改失败!\n");
        return FALSE;
    }
    for (int i = 1; current && i < pos; ++i) {
        current = current->next;
    }
    if(!current){
        return FALSE;
    }
    current->city = elemType;
    return TRUE;
}
5. 查找结点
//按位查找
ElemType * GetElemByPosition(LinkedList * linkedList, int pos)
{
    if(pos < 0 && pos > GetLength(linkedList)){
        printf("位置不合法,查找失败!\n");
        return NULL;
    }

    Node * current = linkedList->head;
    for (int i = 1; current && i < pos; ++i) {
        current = current->next;
    }
    if(!current){
        printf("修改失败!\n");
        return NULL;
    }
    return &current->city;
}
//按值查找
int GetElemByValue(LinkedList * linkedList, ElemType * elemType)
{
    if(!linkedList->head || !elemType){
        printf("查找失败!\n");
        return NO_FOUND;
    }
    Node * current = linkedList->head;
    int pos = 1;
    while (current){
        if(current->city.id == elemType->id)
            if(current->city.name == elemType->name)
                if(current->city.abbreviation == elemType->abbreviation)
                    return pos;
        current = current->next;
        pos++;
    }
    printf("未找到符合元素!\n");
    return NO_FOUND;
}
6. 其他操作
//返回求前驱结点
ElemType GetPreElem(LinkedList * linkedList, ElemType * elemType)
{
    ElemType data;
    data.id = NO_EXIST_ID;
    int pos = GetElemByValue(linkedList, elemType);
    if(pos == 1){
        printf("该结点为首元结点,没有前驱结点!\n");
        return data;
    }
    Node * preNode = linkedList->head;
    for (int i = 1; i < pos-1; ++i) {
        preNode = preNode->next;
    }
    data = preNode -> city;
    return data;
}
//返回后继结点
ElemType GetNextElem(LinkedList * linkedList, ElemType * elemType)
{
    ElemType data;
    data.id = NO_EXIST_ID;
    int pos = GetElemByValue(linkedList, elemType);
    if(pos == GetLength(linkedList)){
        printf("该结点为尾元结点,没有后继结点!\n");
        return data;
    }
    Node * nextNode = linkedList->head;
    for (int i = 0; i < pos; ++i) {
        nextNode = nextNode->next;
    }
    data = nextNode->city;
    return data;
}
//创建新结点
Node * newNode(ElemType * elemType)
{
    Node * node = (Node *) malloc(sizeof(Node));
    node->city = *elemType;
    node->next = NULL;
    return node;
}

//清空链表
void ClearLinkedList(LinkedList * linkedList)
{
    Node * preNode;
    Node * current = linkedList->head;
    while (current){
        preNode = current;
        current = current->next;
        free(preNode);
    }
    linkedList->head = NULL;
    linkedList->length = 0;
}

//返回链表的长度
int GetLength(LinkedList * linkedList)
{
    return linkedList->length;
}

//判断链表是否为空
int IsEmpty(LinkedList * linkedList)
{
    return linkedList->length == 0 ? TRUE : FALSE;
}

//打印链表
void printLinkedList(LinkedList * linkedList)
{
    Node * current = linkedList->head;
    if(!linkedList->head)
    {
        printf("链表为空!\n");
        return;
    }
    while(current){
        printf("[%d, %s, %s]\n", current->city.id, current->city.name, current->city.abbreviation);
        current = current->next;
    }
}
四、测试代码
1. 测试数据
City array[] = {
        {1, "山西省", "晋"},
        {2, "黑龙江省", "黑"},
        {3, "吉林省", "吉"}
};
2. 测试函数
void testLinkedList()
{
    printf("======== 初始化 ========\n");
    printf("打印链表:\n");
    LinkedList linkedList;
    InitList(&linkedList, array, 3);
    printLinkedList(&linkedList);

    City city;
    Node * node = (Node *) malloc(sizeof(Node));
    node->next = NULL;
    city.id = 4;
    city.name = "四川省";
    city.abbreviation = "川";
    node->city = city;

    //1. 插入
    printf("\n======== 插入结点 ========\n");
    InsertElem(&linkedList, 2, city);
    printf("插入后:\n");
    printLinkedList(&linkedList);
    
    //2. 删除
    ElemType del_elem = DeleteElem(&linkedList, 1);
    printf("\n======== 删除结点 ========\n");
    printf("删除首元结点后:\n");
    printLinkedList(&linkedList);
    printf("删除的元素是:[%d, %s, %s]\n", del_elem.id, del_elem.name, del_elem.abbreviation);
    
    //3. 修改
    printf("\n======== 修改结点 ========\n");
    City city1 = {5, "江西省", "赣"};
    UpdateElem(&linkedList, 2, city1) == TRUE ? printf("修改成功!\n") : printf("修改失败!\n");
    printf("修改第二个结点后:\n");
    printLinkedList(&linkedList);

    //按值查找
    printf("\n======== 按值查找 ========\n");
    if(GetElemByValue(&linkedList, &city1) != NO_FOUND )
        printf("city1 {5, \"江西省\", \"赣\"}的位置是:%d\n", GetElemByValue(&linkedList, &city1));

    //按位查找
    printf("\n======== 按位查找 ========\n");
    ElemType * city2 = (ElemType *) malloc(sizeof(ElemType));
    city2 = GetElemByPosition(&linkedList, 3);
    printf("第三个位置的元素city2是:[%d, %s, %s]\n", city2->id, city2->name, city2->abbreviation);

    //求前驱
    printf("\n======== 求city2的前驱结点 ========\n");
    ElemType city3 = GetPreElem(&linkedList, city2);
    if(city3.id != NO_EXIST_ID)
        printf("city2的前驱元素是:[%d, %s, %s]\n", city3.id, city3.name, city3.abbreviation);
        
    //求后继
    printf("\n======== 求city2的后继结点 ========\n");
    ElemType city4 = GetNextElem(&linkedList, city2);
    if(city4.id != NO_EXIST_ID)
        printf("city2的后继元素是:[%d, %s, %s]\n", city4.id, city4.name, city4.abbreviation);

    //清空链表
    printf("\n======== 清空链表 ========\n");
    ClearLinkedList(&linkedList);
    printf("打印链表:\n");
    printLinkedList(&linkedList);

}
3. 测试结果
======== 初始化 ========
打印链表:
[1, 山西省,]
[2, 黑龙江省,]
[3, 吉林省,]

======== 插入结点 ========
插入后:
[1, 山西省,]
[4, 四川省,]
[2, 黑龙江省,]
[3, 吉林省,]

======== 删除结点 ========
删除首元结点后:
[4, 四川省,]
[2, 黑龙江省,]
[3, 吉林省,]
删除的元素是:[1, 山西省,]

======== 修改结点 ========
修改成功!
修改第二个结点后:
[4, 四川省,]
[5, 江西省,]
[3, 吉林省,]

======== 按值查找 ========
city1 {5, "江西省", "赣"}的位置是:2

======== 按位查找 ========
第三个位置的元素city2是:[3, 吉林省,]

======== 求city2的前驱结点 ========
city2的前驱元素是:[5, 江西省,]

======== 求city2的后继结点 ========
该结点为尾元结点,没有后继结点!

======== 清空链表 ========
打印链表:
链表为空!

如有差错或不足请评论指正,感谢观看!!!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值