IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> 简单的线性表的顺序表示实现,以及线性表的链式表示和实现、带头节点的单向链表,C语言简单实现一些基本功能 -> 正文阅读

[C++知识库]简单的线性表的顺序表示实现,以及线性表的链式表示和实现、带头节点的单向链表,C语言简单实现一些基本功能

一、线性表介绍

1、线性结构

在数据元素的非空有限集中:

  • 存在唯一的一个被称做“第一个”的数据元素
  • 存在唯一的一个被称做“最后一个”的数据元素
  • 除第一个之外,集合中的每个数据元素均只有一个前驱
  • 除最后一个之外,集合中的每个数据元素均只有一个后继

2、线性表

????????线性表是n个数据元素的有限序列,同一线性表中的元素必定具有相同特性,相阾的数据元素之间存在着序偶关系。

????????线性表中元素的个数n(n>=0)定义为线性表的长度,0==n时称为空表,在非空表中每个数据元素都有一个确定的位置(下标)。

线性表是一个相当灵活的数据结构,它的长度可根据需要增长或缩短。

二、线性表的顺序表示和实现:

线性表的顺序表示指的是用一组地址连续的存储单元依次存储线性表的数据元素。

一些基本功能的c语言实现:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#define TYPE int
?
// 设计数据结构
typedef struct Array
{
    TYPE* base; // 存储元素的基址
    size_t cnt; // 元素的数量
    size_t cap; // 表的容量
}Array;
?
// 创建线性表,调用者需要提供容量参数,成功返回线性表指针
Array* creat_array(size_t cap)
{
    // 为线性表创建内存空间
    Array* array = malloc(sizeof(Array));
    // 创建存储元素的内存空间
    array->base = malloc(sizeof(TYPE)*cap);
    // 初始化数量成员
    array->cnt = 0;
    // 备份线性表的容量
    array->cap = cap;
    // 返回线性表指针
    return array;
}
?
// 销毁线性表,调用者只需要提供线性表指针即可
void destory_array(Array* array)
{
    // 释放存储元素的内存空间
    free(array->base);
    // 释放线性表内存空间
    free(array);
}
?
// 清理所有元素
void clear_array(Array* array)
{
    array->cnt = 0;
}
?
// 判断线性表是否是空表
bool empty_array(Array* array)
{
    return 0 == array->cnt;
}
?
// 求线性表的长度
size_t length_array(Array* array)
{
    return array->cnt;
}
?
// 访问指定位置的元素
bool get_array(Array* array,int index,TYPE* elemp)
{
    if(index >= array->cnt)
        return false;
?
    *elemp = array->base[index];
    return true;
}
?
// 在末尾添加元素
void add_back_array(Array* array,TYPE elem)
{
    // 容量如果不够用
    if(array->cnt >= array->cap)
    {
        // 容量扩展两倍
        array->cap *= 2;
        // 元素存储空间扩展两倍
        array->base = realloc(array->base,sizeof(TYPE)*array->cap);
    }
?
    // 末尾添加元素
    array->base[array->cnt++] = elem;
}
?
// 插入元素
bool insert_array(Array* array,int index,TYPE elem)
{
    // 如果下标不合法则插入失败
    if(index >= array->cnt)
        return false;
?
    // 把最后一个元素添加到末尾
    add_back_array(array,array->base[array->cnt-1]);
?
    // 把index后面的元素整体移动一个位置
    /*
    for(int i=array->cnt-2; i>index; i--)
    {
        array->base[i] = array->base[i-1];
    }
    */
?
    // 线性表的顺序存储才可以使用
    memmove(array->base+index+1,array->base+index,sizeof(TYPE)*(array->cnt-index-2));
    
    array->base[index] = elem;
    return true;
}
?
// 删除元素,按位置删除
bool delete_index_array(Array* array,int index)
{
    if(index >= array->cnt)
        return false;
?
    memmove(array->base+index,array->base+index+1,sizeof(TYPE)*(array->cnt-index-1));
    array->cnt--;
    return true;
}
?
// 查询元素
int query_array(Array* array,TYPE elem,int (*compare)(const void*,const void*))
{
    for(int i=array->cnt-1; i>=0; i--)
    {
        if(!compare(&elem,array->base+i))
            return i;
    }
    return -1;
}
?
// 删除元素,按值删除
bool delete_value_array(Array* array,TYPE elem,int (*compare)(const void*,const void*))
{
    return delete_index_array(array,query_array(array,elem,compare));
}
?
// 对线性表进行排序
void sort_array(Array* array,int (*compare)(const void*,const void*))
{
    bool flag = true;
    for(int i=array->cnt-1; flag && i>0; i--)
    {
        flag = false;
        for(int j=0; j<i; j++)
        {
            if(1 == compare(array->base+j,array->base+j+1))
            {
                TYPE tmp = array->base[j];
                array->base[j] = array->base[j+1];
                array->base[j+1] = tmp;
                flag = true;
            }
        }
    }
}
?
// 遍历线性表,只是为了测试
void show_array(Array* array)
{
    for(int i=0; i<array->cnt; i++)
    {
        printf("%d ",array->base[i]);
    }
    printf("\n");
}
?
int main(int argc,const char* argv[])
{
    Array* array = creat_array(10);
    for(int i=0; i<10; i++)
    {
        add_back_array(array,rand()%100);
    }
    
    show_array(array);
?
    int intcmp(const void* p1,const void* p2)
    {
        if(*(int*)p1 > *(int*)p2)
            return 1;
        if(*(int*)p1 < *(int*)p2)
            return -1;
        return 0;
?
    }
    
    delete_value_array(array,77,intcmp);
?
    sort_array(array,intcmp);
    
    show_array(array);
    destory_array(array);
    return 0;
}

三、线性表的链式表示和实现

????????线性表的顺序存储结构的特点是逻辑关系上相邻的两个元素在物理位置上也相邻,因此可以随机存取表中的任一元素,它的存储位置可以用一个简单、直观的公式来表示。

????????这个特点也铸成了这种存储结构的缺点:在插入、删除操作时,需要移动大量的元素。而线性表的另一种表示方法——链存储结构刚好弥补了它的缺点。

????????链存储结构不要求逻辑上相邻的元素在物理位置上也相邻,因此它没有顺序存储结构所有具有的缺点,但同时也失去了顺序存储结构可随机存取的优点。

????????链存储结构的特点是元素可以使用存储内存中的任何位置(可以是连续的,也可以不连续),元素a[i]和a[i+1]的逻辑关系不依靠相对位置,而是元素中增加一个指示其后继元素的数据(元素指针),元素本身的数据+后继信息构成了存储映像,俗称节点(node)。

typedef struct Node
{
    TYPE data;  // 数据域
    struct Node* next;  // 指针域
}Node;

?????????若干个元素节点通过指针域连接起来,形成的线性表结构称为链式表,简称链表,节点中只有一个指向后继元素的指针域,这种链表也被称为单向链表。

????????单向链表必须有一个指向第一个节点的指针,被称为头指针,被它指向的节点也被称为头节点,头节点可以不存储数据,单纯的作为一个占位节点,最后一个节点指向空,作为结束标志。

1、不带头节点的单向链表

#include <stdio.h>
#include <stdlib.h>
#define TYPE int
?
typedef struct Node
{
    TYPE data;
    struct Node* next;
}Node;
?
// 创建节点
Node* create_node(TYPE data)
{
    // 创建节点内存
    Node* node = malloc(sizeof(Node));
    // 赋值数据域
    node->data = data;
    // 初始化指针域
    node->next = NULL;
    return node;
}
?
// 头添加元素
Node* front_list(Node* head,TYPE data)
{
    // 创建节点
    Node* node = create_node(data);
    node->next = head;
    return node;
}
?
// 删除元素
Node* delete_index_list(Node* head,int index)
{
    // 删除每个节点,因为第一个节点没有前驱
    if(0 == index)
    {
        Node* node = head;
        head = head->next;
        free(node);
        return head;
    }
?
    // 找到要删除的节点的前驱
    Node* prev = head;
    while(NULL!=prev->next && index-->1)
            prev = prev->next;
?
    if(NULL != prev->next)
    {
        // 备份要删除的节点
        Node* node = prev->next;
        // 前驱节点的指针域指向后继节点
        prev->next = prev->next->next;
        free(node);
    }
?
    return head;
}
?
// 插入元素
Node* insert_list(Node* head,int index,TYPE data)
{
    Node* node = create_node(data);
    if(0 == index)
    {
        node->next = head;
        return node;
    }
?
    Node* prev = head;
    while(NULL!=prev->next && index-->1)
            prev = prev->next;
    
    node->next = prev->next;
    prev->next = node;
    return head;
}
?
// 遍历链表
void show_list(Node* head)
{
    /*
    Node* node = head;
    while(NULL != node)
    {
        printf("%d ",node->data);
        node = node->next;
    }
    printf("\n");
    */
    for(Node* n=head; NULL!=n; n=n->next)
    {
        printf("%d ",n->data);
    }
    printf("\n");
}
?
int main(int argc,const char* argv[])
{
    // 创建头指针
    Node* head = NULL;
    for(int i=0; i<10; i++)
    {
        head = front_list(head,i);
    }
    show_list(head);
    head = delete_index_list(head,0);
    show_list(head);
    return 0;
}

在执行链表的插入、删除操作时,需要被操作节点的前驱节点和后继节点,如果被操作节点是头节点,则它没有前驱节点,需要额外特殊处理,因此为了方便插入和删除操作所以给单链增加一个空的头节点。

2、带头节点的单向链表

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
?
#define TYPE int
?
typedef struct Node
{
    TYPE data;
    struct Node* next;
}Node;
?
Node* create_node(TYPE data)
{
    Node* node = malloc(sizeof(Node));
    node->data = data;
    node->next = NULL;
    return node;
}
?
Node* create_list(void)
{
    Node* node = malloc(sizeof(Node));
    node->next = NULL;
    return node;
}
?
void destory_list(Node* head)
{
    while(head)
    {
        Node* node = head;
        head = head->next;
        free(node);
    }
}
?
void front_list(Node* head,TYPE data)
{
    Node* node = create_node(data);
    node->next = head->next;
    head->next = node;
}
?
bool delete_index_list(Node* head,int index)
{
    // 找到要删除的节点的前驱
    Node* prev = head;
    while(NULL != prev->next && index-- >= 1)
        prev = prev->next;
?
    // index 非法,超出了节点的数量
    if(NULL == prev->next)
        return false;
?
    // 备份要删除的节点
    Node* node = prev->next;
    // 前驱节点接后继节点
    prev->next = prev->next->next;
    // 删除节点
    free(node);
    return true;
}
?
void show_list(Node* head)
{
    // 要跳过头节点
    for(Node* n=head->next; NULL!=n; n=n->next)
    {
        printf("%d ",n->data);
    }
    printf("\n");
}
?
int main(int argc,const char* argv[])
{
    Node* list = create_list();
    for(int i=0; i<10; i++)
    {
        front_list(list,i);
    }
    show_list(list);
    delete_index_list(list,3);
    show_list(list);
    return 0;
}

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2022-08-06 10:25:04  更:2022-08-06 10:28:53 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年4日历 -2024/4/19 20:36:11-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码