c语言链表升级

之前的链表就是一个普通的带头的单向链表,我们不自觉的会发现这样的链表有缺陷,有关链表的删除新增查找跟链表的结构体内容耦合性太强

什么意思呢?

比如我们之前的链表的结构体

typedef struct _Teacher
{
    int age;
    struct _Teacher *next;
}Teacher;

我们有关链表所有的操作都跟这个结构体紧密的相连,如果此刻我们有另一个结构体,里面的字段都跟这个不一样,那么,我们可能还需要对这个新的结构体写一套链表操作?

相当于下面的图,呵呵,有点丑

那么我们的解决方案是什么呢?,我们能不能关于不同结构体的所有操作都有一套公共方法呢?

当然是可以的!

在这之前我们必须有一个概念,结构体中的第一个元素的地址就是代表结构体的地址。

我们设计一个单纯代表数据结构的结构体,这个结构体只有下个对象的地址的指针成员

typedef struct _tag_LinkListNode
{
    void * next;
}LinkListNode;

比如此刻我们有一个教师结构体,那么我们只需要在结构体的第一个成员是上面的LinkListNode对象。
形成下面的数据结构:

typedef struct _Teacher
{
    LinkListNode listNode;
    int age;
    char name[50];
}Teacher;

那么,此刻我们只要在我们的对链表的操作将传过来的Teacher * 指针强转为LinkListNode *类型,查询出来的LinkListNode * 指针变量再强转为Teacher * 对象,从而对LinkListNode形成一套插入删除查询的api就可以了
以下是实现的代码:

接口:

#ifndef _MYLINKLIST_H_
#define _MYLINKLIST_H_

typedef void LinkList;

typedef struct _tag_LinkListNode
{
    struct _tag_LinkListNode* next;
}LinkListNode;

LinkList* LinkList_Create();

void LinkList_Destroy(LinkList* list);

void LinkList_Clear(LinkList* list);

int LinkList_Length(LinkList* list);

int LinkList_Insert(LinkList* list, LinkListNode* node, int pos);

LinkListNode* LinkList_Get(LinkList* list, int pos);

LinkListNode* LinkList_Delete(LinkList* list, int pos);

#endif

实现:

#include "stdio.h"
#include "stdlib.h"
#include "linklist.h"

typedef struct _tag_LinkList
{
    //头节点
    LinkListNode header;
    int length;
}TLinkList;

/************************************************************************/
/* 创建list并初始化一个头节点                                           */
/************************************************************************/
LinkList* LinkList_Create()
{
    TLinkList *tlist = (TLinkList *)malloc(sizeof(TLinkList));
    if (tlist == NULL)
    {
        return NULL;
    }
    tlist->length = 0;
    tlist->header.next = NULL;
    return tlist;
}

void LinkList_Destroy(LinkList* list)
{
    if (list == NULL)
    {
        return;
    }
    free(list);
}
/************************************************************************/
/* 清空list链表                                                         */
/************************************************************************/
void LinkList_Clear(LinkList* list)
{
    if (list == NULL)
    {
        return;
    }
    TLinkList *tlinkList = (TLinkList *)list;
    tlinkList->length = 0;
    tlinkList->header.next = NULL;

}

int LinkList_Length(LinkList* list)
{
    if (list == NULL)
    {
        return 0;
    }
    TLinkList *tlinkList = (TLinkList *)list;

    return tlinkList->length;
}

int LinkList_Insert(LinkList* list, LinkListNode* node, int pos)
{
    LinkListNode *pre, *cur;
    int i;
    if (list == NULL || node == NULL)
    {
        return -1;
    }
    //校验下标
    if (pos<0)
    {
        return -2;
    }
    TLinkList *tlinkList = (TLinkList *)list;
    if (pos>tlinkList->length - 1)
    {
        pos = tlinkList->length;
    }
    pre = (LinkListNode *)list;//初始化指向头节点
    cur = tlinkList->header.next;//初始化指向第一个节点,如果空链表指向空
    for (i = 0; i < pos; i++)
    {
        pre = cur;
        cur = cur->next;//最终让当前指针指向要插入的位置
    }
    pre->next = node;
    node->next = cur;

    tlinkList->length++;
    return 0;
}

LinkListNode* LinkList_Get(LinkList* list, int pos)
{
    LinkListNode *pre, *cur;
    int i;
    if (list == NULL)
    {
        return NULL;
    }
    TLinkList *tlinkList = (TLinkList *)list;
    //校验下标
    if (pos >= tlinkList->length || pos < 0)
    {
        return NULL;
    }
    cur = tlinkList->header.next;//初始化指向第一个节点,如果空链表指向空
    for (i = 0; i < pos; i++)
    {
        cur = cur->next;
    }

    return cur;
}

LinkListNode* LinkList_Delete(LinkList* list, int pos)
{
    LinkListNode *pre, *cur;
    int i;
    if (list == NULL)
    {
        return NULL;
    }
    TLinkList *tlinkList = (TLinkList *)list;
    //校验下标
    if (pos >= tlinkList->length || pos < 0)
    {
        return NULL;
    }
    pre = (LinkListNode *)list;//初始化指向头节点
    cur = tlinkList->header.next;//初始化指向第一个节点,如果空链表指向空
    for (i = 0; i < pos; i++)
    {
        pre = cur;
        cur = cur->next;
    }
    pre->next = cur->next;
    LinkListNode* curnode = cur;
    tlinkList->length--;
    return curnode;
}

测试代码

#include "stdio.h"
#include "stdlib.h"
#include "linklist.h"

typedef struct _Teacher
{
    LinkListNode listNode;
    int age;
    char name[50];
}Teacher;
void main()
{
    LinkList* linkList;
    Teacher t1, t2, t3;
    int len;
    int i;
    linkList = LinkList_Create();
    t1.age = 11;
    t2.age = 22;
    t3.age = 33;
    LinkList_Insert(linkList, (LinkListNode*)&t1, 0);
    LinkList_Insert(linkList, (LinkListNode*)&t2, 0);
    LinkList_Insert(linkList, (LinkListNode*)&t3, 0);

    len = LinkList_Length(linkList);

    for (i = 0; i < len; i++)
    {
        Teacher * t = (Teacher *)LinkList_Get(linkList, i);
        printf("cur teacher age=%d\n", t->age);
    }

    LinkList_Delete(linkList, 0);
    LinkList_Delete(linkList, 1);
    len = LinkList_Length(linkList);
    for (i = 0; i < len; i++)
    {
        Teacher * t = (Teacher *)LinkList_Get(linkList, i);
        printf("cur teacher age=%d\n", t->age);
    }
    system("pause");

}

接下来,如果我们有新的结构体,只要在测试代码那边做修改,而不需要动我们的核心代码了。

时间: 2024-11-10 20:36:01

c语言链表升级的相关文章

C语言-链表

单向链表:结构体非常适合链表结构,链表的组成:head指针.数据块节点指针p->nest.结束指针NULL. 链表操作:需要首先找到表头head指针.链表的操作包括动态链表的创建.顺序输出.删除节点.插入节点的操作. 动态存储操作函数:(ANSI规则返回的指针类型为void*,早期的为字符型指针) 分配一个块:void *malloc(unsigned int size)  //分配成功则返回指向起始地址的指针void * 分配多个块:void *calloc(unsigned n,unsign

关于c语言链表的操作

这几天又讲到链表了,但是又忘记了,所以重新把关于链表的建链表,对链表进行排序,然后是删除,插入,以及遍历等功能..但是最近要考试了,所以没有写成菜单的形式..等考试完了,在进行补充吧.. 代码如下... #include<stdio.h> #include<stdlib.h> #include<string.h> struct node { int data; struct node *next; }; int main() { /*建立链表操作*/ int n,x,p

C语言链表实例--玩转链表

下图为最一简单链表的示意图: 第 0 个结点称为头结点,它存放有第一个结点的首地址,它没有数据,只是一个指针变量.以下的每个结点都分为两个域,一个是数据域,存放各种实际的数据,如学号 num,姓名 name,性别 sex 和成绩 score 等.另一个域为指针域,存放下一结点的首地址.链表中的每一个结点都是同一种结构类型. 指针域: 即在结点结构中定义一个成员项用来存放下一结点的首地址,这个用于存放地址的成员,常把它称为指针域. 在第一个结点的指针域内存入第二个结点的首地址,在第二个结点的指针域

c语言链表和指针的运用

在学习指针之前,首先要认识指针.指针是一个存储计算机内存地址的变量.从指针指向的内存读取数据称作指针的取值.指针可以指向某些具体类型的变量地址,例如int.long和double.指针也可以是void类型.NULL指针和未初始化指针. 根据出现的位置不同,操作符 * 既可以用来声明一个指针变量,也可以用作指针的取值.当用在声明一个变量时,*表示这里声明了一个指针.其它情况用到*表示指针的取值.&是地址操作符,用来引用一个内存地址.通过在变量名字前使用&操作符,我们可以得到该变量的内存地址.

C语言链表的来源分析

C语言中的链表是重点,也是难点,而且意义非凡.对链表的的抽象和恐惧是源于对它的来龙去脉的不明白.所以很有必要对它的发展渊源做透彻分析. 链表的单位是节点,而节点源于复合数据类型:结构体: 节点和结构体的区别就是看是否有指针域,目的就是想找到下一个节点: 结构体形如: struct Ghost { char name[30]; int age; int height; char addr[30]; }; 节点形如: struct Ghost { char name[30]; int age; in

注释最全的C语言链表的增删改查

1 //这是C语言的写法,但会报错,原因是len(当前的节点长度) 2 //无法在insert(插入)和deleted(删除)之后改变 3 //不能使用delete是因为delete是C++中的一个运算符 4 //最终我把改程序用C++写了一遍,运用引用将len的真实值改变了 5 #include <stdio.h> 6 #include <stdlib.h> 7 typedef int ElementType; 8 typedef struct node { 9 ElementT

C语言链表

#define _CRT_SECURE_NO_WARNINGS #include "stdio.h" #include "stdlib.h" typedef struct _Teacher { int age; struct _Teacher *next; }Teacher; Teacher* create_teacher(); int print_teacher(Teacher * pHeader); int add_teacher(Teacher * pHead

C语言----------链表的简单操作

#include <stdio.h> #include <malloc.h> typedef struct node{ //定义节点类型 char data; //数据域 struct node *next; //指针域 }linklist; linklist* Create(){ //创建链表 char key; linklist *phead; //头指针 linklist *pnew; //新节点 linklist *pend; //尾指针 phead = (linklist

C语言链表的简单实用

// //  ViewController.m //  链表 // //  Created by 张凯泽 on 16/1/26. //  Copyright © 2016年 rytong_zkz. All rights reserved. // #import "ViewController.h" /* static 关键字的作用: (1)函数体内 static 变量的作用范围为该函数体,不同于 auto 变量,该变量的内存只被分配一次, 因此其值在下次调用时仍维持上次的值: (2)在