数据结构之线性表

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了数据结构之线性表相关的知识,希望对你有一定的参考价值。

No.1 简介

线性表是最常用的一种数据结构

特点:

  • 存在唯一一个第一个的数据元素

  • 存在唯一一个最后一个的数据元素

  • 除第一个之外,集合中的每个元素都只有一个前驱

  • 除最后一个之外,集合中的每个元素都只有一个后继

数据对象:欲操作的数据类型,可以是C语言中基本的数据类型,也可以是自定义的数据类型
数据关系:数据类型逻辑关系
基本操作:有了数据,他们之间有着一定的联系,那么必然要对它们进行一系列的操作,数据操作就是执行这些操作的集合

No.2 线性表的顺序表示和实现

/* 添加头文件 */
#include <stdio.h>
//添加malloc函数的头文件
#include <malloc.h>
#include <windows.h>

/* 预定义常量和类型 */
#define TRUE 1
#define FALSE 0
#define OK 1
#define ERROR 0
#define INFEASIBLE -1
#define OVERFLOW -2
#define LIST_INIT_SIZE 100 //线性表的存储空间的初始分配量
#define LISTINCREMENT 10 //线性表的存储空间的的分配增量

typedef int Status;
typedef int ElemType;
typedef struct {
    ElemType *elem;
    int length;
    int listsize;
} SqList;

//初始化线性表,主要是初始化结构体参数
Status InitList(SqList &L) {
    //构造一个线性表L
    L.elem = (ElemType *) malloc(LIST_INIT_SIZE * sizeof(ElemType));
    if (!L.elem) {
        exit(OVERFLOW); //存储分配失败,退出程序
    }
    L.length = 0; //空表的长度为0
    L.listsize = LIST_INIT_SIZE; //初始存储容量
    return OK;
}

// 创建线性表,主要的功能是 为初始化函数线性表 所申请的内存,初始化数据
Status CreateList(SqList &L, int n) {
    ElemType *newbase;
    int i;
    if (n < 0) {
        return ERROR;
    }
    if (n > L.listsize) {
        //realloc函数用于修改一个原先已经分配的内存块的大小,可以使一块内存的扩大或缩小,重新分配一块比原来大n个内存空间的线性表,并原先线性表的数据复制到新线性表中
        newbase = (ElemType *) realloc(L.elem, (L.listsize + n) * sizeof(ElemType));
        if (!newbase) {
            exit(OVERFLOW);
        }
        L.elem = newbase; //将新线性表的引用赋值给L.elem
        L.listsize += n;
    }
    L.length = n;
    printf("请输入数据,用空格分隔:
");
    for (i = 0; i < n; i++) {
        scanf("%d", &(L.elem[i]));
    }
    return OK;
}

//在线性表 指定位置插入一个数据
Status ListInsert(SqList &L, int i, ElemType e) {
    ElemType *newbase, *q, *p;
    if (i < 1 || i > L.length) {
        return ERROR;
    }
    if (L.length > L.listsize) {
        newbase = (ElemType *) realloc(L.elem, (L.listsize + LISTINCREMENT) * sizeof(ElemType));
        if (!newbase) {
            exit(OVERFLOW);
        }
        L.elem = newbase;
        L.listsize += LISTINCREMENT;
    }
    q = &(L.elem[i - 1]);
    for (p = &(L.elem[L.length - 1]); p >= q; --p) {
        printf("*p=%d
",*p);
        printf("*(p+1)=%d
",*(p+1));
        *(p + 1) = *p;
    }
    *q = e;
    ++L.length;
    return OK;
}

//删除线性表指定位置的数值,并且返回删除的数据,保存在 e
Status ListDelete(SqList &L, int i, ElemType &e) {
    ElemType *p, *q;
    if ((i < 1) || (i > L.length)) {
        return ERROR;
    }
    p = &(L.elem[i - 1]);
    e = *p;
    q = L.elem + L.length - 1; //q取线性表最末尾的地址
    for (++p; p <= q; ++p) {
        *(p - 1) = *p;
    }
    --L.length;
    return OK;
}

//查找线性表中的 是否包含某个值,返回满足条件的第一个位置
// 这个函数比较特殊,它的最后一个参数比较特别,是传入的函数指针
Status compare(ElemType a, ElemType b) {
    if (a == b) {
        return TRUE;
    }
    return FALSE;
}

int LocateElem(SqList L, ElemType e, Status(*compare)(ElemType, ElemType)) {
    int i;
    ElemType *p;
    i = 1;
    p = L.elem;
    while (i <= L.length && !(*compare)(*p++, e)) {
        ++i;
    }
    if (i <= L.length) {
        return i;
    }
    return 0;
}

//删除线性表,主要功能是将 malloc 或者 realloc  函数分配而来的内存释放掉
Status DestroyList(SqList &L) {
    if (L.elem) {
        free(L.elem);
    }
    printf("线性表已销毁!");
    return OK;
}

//打印函数,功能是将线性表中的数据 输出到屏幕
Status ShowList(SqList &L) {
    int i;
    if (L.length <= 0) {
        return ERROR;
    }
    for (i = 0; i < L.length; i++) {
        printf("%d	", L.elem[i]);
    }
    printf("
");
    return OK;
}

int main() {
    //测试函数
    SqList L;
    //测试初始化函数
    InitList(L);
    printf("%d	%d
", L.length, L.listsize);
    //添加函数,并显示出来
    CreateList(L, 5);
    ShowList(L);
    //测试插入函数
    ListInsert(L, 2, 88);
    printf("插入后的线性表:
");
    ShowList(L);
    //测试删除函数
    int e;
    ListDelete(L, 3, e);
    printf("删除后的线性表:
");
    ShowList(L);
    printf("删除的数据是:%d
", e);
    //测试LocateElem函数
    int i = LocateElem(L, 2, compare);
    printf("要查询的数据位于线性表的位置为:%d
", i);
    //删除线性表
    DestroyList(L);
    return 0;
}

No.3 函数指针

函数指针是指向函数的指针,确切的说,是指向函数类型为指针类型的函数,函数指针用来保存函数首地址,即可以通过指针访问函数,函数指针相当于取别名,函数指针可以指向一类函数,即k可以重复赋值

  • int function(int x); 申明一个函数
  • int (*p)(int x); 申明一个函数指针
  • p = function; 将函数的首地址赋值给指针
  • int c = (*p)(3); 调用函数
  • int *func(int x); 声明一个函数指针
#include <stdio.h>

int function(int n){
    return n * n;
}

int main() {
    int (*p)(int x); //申明一个函数指针
    p = function; //初始化,将函数的首地址赋值给指针p
    int c = (*p)(3);
    printf("%d",c);
    return 0;
}

#include <stdio.h>

void function1()
{
    printf("function1
");
}

void function2()
{
    printf("function2
");
}

int main() {
    void (*p)(); //申明一个函数指针
    p = function1; //初始化,将函数的首地址赋值给指针p
    p();
    p = function2;
    p();
}

No.4 线性表的链式实现

通过上文,我们知道:

线性表的顺序存储结构的特点是逻辑关系上相邻的两个元素在物理位置上也相同,因此线性表的顺序存储结构可以随意的访问线性表中的任意元素,然后,这个特点也正是造成了在插入或者删除时,需要移动大量元素的缺点,

所以,线性表的另一种存储结构出现了,它就是链式存储结构,由于它不要求逻辑上相邻的两个元素物理上必须相邻,因此他没有顺序存储结构所具备的缺点,但是同样失去了随机存取的优点

/* 添加头文件 */
#include <stdio.h>
//添加malloc函数的头文件
#include <malloc.h>
#include <windows.h>

/* 预定义常量和类型 */
#define TRUE 1
#define FALSE 0
#define OK 1
#define ERROR 0
#define INFEASIBLE -1
#define OVERFLOW -2
#define LIST_INIT_SIZE 100 //线性表的存储空间的初始分配量
#define LISTINCREMENT 10 //线性表的存储空间的的分配增量

typedef int Status;
typedef int ElemType;
typedef struct LNode {
    ElemType data;
    struct LNode *next;
} LNode, *LinkList;

//创建链表,头插法
void CreateList_Head(LinkList &L, int n) {
    int i;
    LinkList p;
    L = (LinkList) malloc(sizeof(LNode));
    L->next = NULL;
    printf("请输入 %d 个数据:
", n);
    for (i = n; i > 0; --i) {
        //生成新节点
        p = (LinkList) malloc(sizeof(LNode));
        //输入元素值
        scanf("%d", &p->data);
        //p的指针域指向p的指针域
        p->next = L->next;
        //L的指针域指向p
        L->next = p;
    }
}

//创建链表,尾插法
void CreateList_Last(LinkList &L, int n) {
    int i;
    LinkList p, q;
    L = (LinkList) malloc(sizeof(LNode));
    L->next = NULL;
    p = L;
    printf("请输入 %d 个数据:
", n);
    for (int i = 0; i < n; i++) {
        //生成新节点
        q = (LinkList) malloc(sizeof(LNode));
        //输入元素值
        scanf("%d", &(q->data));
        //q的指针域指向p的指针域
        q->next = p->next;
        //p的指针域指向q
        p->next = q;
        p = q;
    }
}

//在带头结点的单链线性表L中第 i 个位置之前插入元素 e
Status ListInsert(LinkList &L, int i, ElemType e) {
    LinkList p, s; //申明变量类型
    int j;
    p = L;
    j = 0;
    //寻找第i-1个节点
    while (p && j < i - 1) {
        //和p++效果一样
        p = p->next;
        ++j;
    }
    if (!p || j > i - 1) {
        return ERROR;
    }
    //生成新节点。插入到L中
    s = (LinkList) malloc(sizeof(LNode));
    //将e的值赋值给s的数据域
    s->data = e;
    //将s的指针域指向p的指针域
    s->next = p->next;
    //p的指针域指向s
    p->next = s;
    return OK;
}

//删除元素,并返回元素值
Status ListDelete(LinkList &L, int i, ElemType &e) {
    LinkList p, q;
    int j;
    p = L;
    j = 0;
    //寻找第i个节点
    while (p->next && j < i - 1) {
        p = p->next;
        ++j;
    }
    if (!(p->next) && j > i - 1) {
        return ERROR;
    }
    //删除并释放节点
    q = p->next;
    //p的指针域指向q的指针与
    p->next = q->next;
    //q的数据域赋值给e
    e = q->data;
    //释放q这个节点
    free(q);
    return OK;
}

//查找给定节点的元素
Status GetElem(LinkList L, int i, ElemType &e) {
    LinkList p;
    int j;
    p = L->next; //p指向第一个节点
    j = 1;
    while (p && j < i) {
        p = p->next;
        ++j;
    }
    if (!p || j > i) {
        return ERROR;
    }
    e = p->data;
    return OK;
}

void showList(LinkList &L) {
    LinkList p = L->next;
    while (p != NULL) {
        printf("%d	", p->data);
        p = p->next;
    }
    printf("
");
}

//删除带有头结点的链表
void DestroyList(LinkList &L) {
    LinkList p, q;
    p = L->next;
    while (p != NULL) {
        //q指向p的指针域,依次释放节点
        q = p->next;
        free(p);
        p = q;
    }
}

int main() {
    int e;
    LNode node;
    LinkList L = &node;
    CreateList_Last(L, 5);
    printf("创建后的线性表:
");
    showList(L);
    ListInsert(L, 3, 100);
    printf("在第三个位置上插入100:
");
    showList(L);
    ListDelete(L, 1, e);
    printf("删除第一个位置上的值:
");
    showList(L);
    printf("删除的数据是:%d
", e);
    GetElem(L, 3, e);
    printf("当前链表第三个位置上的值是:%d
", e);
    DestroyList(L);
}

以上是关于数据结构之线性表的主要内容,如果未能解决你的问题,请参考以下文章

常见数据结构之线性表

数据结构之线性表之顺序存储Java实现

[DataStructure]非线性数据结构之哈希表二叉树及多叉树 Java 代码实现

数据结构之顺序线性表

数据结构与算法之----线性表

数据结构之线性表再思考