线性表

线性表


线性表简介

线性表存储结构分为顺序存储结构 (顺序表) 和链式存储结构 (链表)。


顺序表

将“具有 ‘一对一’ 逻辑关系的数据按照次序连续存储到一整块物理空间上”的存储结构就是顺序存储结构。

使用顺序表存储数据之前,除了要申请足够大小的物理空间之外,为了方便后期使用表中的数据,顺序表还需要实时记录以下 2 项数据:

  1. 顺序表申请的存储容量;
  2. 顺序表的长度,也就是表中存储数据元素的个数;

提示:正常状态下,顺序表申请的存储容量要大于顺序表的长度。

顺序表初始化

自定义顺序表,C 语言实现代码如下:

typedef struct Table{
    int *head;  //声明一个名为 head 的长度不确定的数组(动态数组)
    int length; //顺序表长度
    int size;   //实际表存储容量
}table;

接下来时初始化(赋值)顺序表。整个顺序表初始化的过程被封装到了一个函数中,此函数返回值是一个已经初始化完成的顺序表。

#define Size 5  //顺序表申请空间大小
table intTable(){
    table t;
    t.head = (int*)malloc(Size*sizeof(int));    //空表,动态申请空间
    if(!t.head) //申请失败,退出
    {
        printf("init failed\n");
        exit(0);
    }
    t.length = 0;   //空表长度初始化为 0
    t.size = Size;  //初始存储空间 Size

    return t;
}
实例
    #include <stdio.h>
    #include <stdlib.h>
    #define Size 5
    typedef struct Table{
        int * head;
        int length;
        int size;
    }table;
    table initTable(){
        table t;
        t.head=(int*)malloc(Size*sizeof(int));
        if (!t.head)
        {
            printf("初始化失败");
            exit(0);
        }
        t.length=0;
        t.size=Size;
        return t;
    }
    //输出顺序表中元素的函数
    void displayTable(table t){
        for (int i=0;i<t.length;i++) {
            printf("%d ",t.head[i]);
        }
        printf("\n");
    }
    int main(){
        table t=initTable();
        //向顺序表中添加元素
        for (int i=1; i<=Size; i++) {
            t.head[i-1]=i;
            t.length++;
        }
        printf("顺序表中存储的元素分别是:\n");
        displayTable(t);
        return 0;
    }

顺序表基本操作

基本操作:增删改查

插入

基本策略

  • 遍历找到要插入的位置
  • 将插入位置及其后续元素整体后移
  • 插入元素
  1. 头插
  2. 中间插
  3. 尾插
//插入,elem 为插入的元素,add为插入位置
table addTable(table t, int elem, int add)
{
    //判断插入位置是否合法,[1,length+1]
    if(add > t.length || add < 1)
    {
        printf("插入位置非法\n");
        return t;
    }
    //还需判断顺序表是否已满,若已满则需申请
    if(t.length == t.size)
    {
        t.head = (int*)realloc(t.head, (t.size+1)*sizeof(int));
        if(!t.head)
        {
            printf("realloc failed\n");
            return t;
        }
        t.size+=1;
    }
    //后移
    for(int i = t.length-1; i >= add-1; i--)
    {
        t.head[i+1] = t.head[i];
    }
    //插入
    t.head[add-1] = elem;
    t.length++;
    
    return t;
}

删除

找到目标元素,后续元素前移

table delTable(table t, int add){
    if(add > t.length || add < 1)
    {
        printf("删除位置非法\n");
        return t;
    }

    for(int i = add; i <= t.length; i++)
    {
        t.head[i-1] = t.head[i];
    }
    t.length--;
    
    return t;
}

查找

可以选择多多种查找算法实现,这里选择顺序查找算法

int sekectTable(table t, int elem){
    for(int i = 0; i < t.length; i++)
    {
        if(elem == t.head[i])
        {
            return i+1;
        }
    }

    return -1;  //查找失败
}

修改

table updateTable(table t, int elem, int newElem){
    int add = selectTable(t, elem);
    if(add == -1)
    {
        printf("未查找到elem\n");
        return t;
    }
    else
    {
        t.head[add-1] = newElem;
    }
    return t;
}

链表

链表的节点

链表中每个数据的存储都由以下两部分组成:

  1. 数据元素本身,其所在的区域称为数据域;
  2. 指向直接后继元素的指针,所在的区域称为指针域;
typedef struct Link{
    char elem;  //代表数据域
    struct Link* next;  //指针域
}link;  //link 为节点名,每个节点都是一个link结构体

头节点,头指针,首元节点

一个完整的链表需要由以下几部分构成:

  1. 头指针:一个普通的指针,它的特点是永远指向链表第一个节点的位置。很明显,头指针用于指明链表的位置,便于后期找到链表并使用表中的数据;
  2. 节点:链表中的节点又细分为头节点、首元节点和其他节点:
  • 头节点:其实就是一个不存任何数据的空节点,通常作为链表的第一个节点。对于链表来说,头节点不是必须的,它的作用只是为了方便解决某些实际问题;
  • 首元节点:由于头节点(也就是空节点)的缘故,链表中称第一个存有数据的节点为首元节点。首元节点只是对链表中第一个存有数据节点的一个称谓,没有实际意义;
  • 其他节点:链表中其他的节点;
    链表初始化

创建一个链表需要做一下工作

  1. 声明一个头指针(如有必要,可以声明一个头节点)
  2. 创建多个存储数据的节点
    实例:创建一个存储 {1,2,3,4} 的链表
代码
//无头节点链表
link* initLink(){
    link* p = NULL; //创建头指针
    link* temp = (link*)malloc(sizeof(link));   //创建首元节点
    //初始化首元节点
    temp->elem = 1;
    temp->next = NULL;
    p = temp;

    for(int i = 2; i < 5; i++)
    {
        link* a = (link*)malloc(sizeof(link));
        a->elem = i;
        a->next = NULL;
        temp->next = a;
        temp = temp->next;    
    }
    return p;
}
//有头节点链表
link* initLink_head(){
    link* p = (link*)malloc(sizeof(link));  //头节点
    link* temp = p; //指针,用于遍历
    for(int i = 1; i < 5; i++)
    {
        link *a = (link*)malloc(sizeof(link));  //创建节点并初始化
        a->elem = i;
        a->next = NULL;
        //链接节点
        p->next = a;
        p = p->next;
    }
    return p;
}

链表的基本操作

增删改查,这里以有头节点的单链表

插入

链表插入元素的思想是固定的

  1. 将新节点的next指向下一个节点
  2. 将前一个节点的next指向新节点
//插入,p为原链表,elem为新数据,add为插入位置
link* insertLink(link* p, int elem, int add){
    link* temp = p; //遍历用
    //找到插入位置上一个节点
    for(int i = 1; i < add; i++)
    {
        if(temp == NULL)
        {
            printf("插入位置非法\n");
            return p;
        }
        temp = temp->next;
    }
    link* c = (link*)malloc(sizeof(link));
    c->elem = elem;
    c->next = temp->next;
    temp->next = c;
    return p;
}
// 在学校学的时候都要分头插,中间插入和尾插

删除

link* delLink(link* p, int add){
    link* temp = p; //遍历
    for(int i = 1; i < add; i++)
    {
        temp = temp->next;
    }
    link* del = temp->next; //单独设置一个指针指向被删除节点
    temp->next = temp->next->next;
    free(del);  //手动释放该节点
    return p;
}
//跟学校学的相比,少了防错,多了手动释放

查找

int selectLink(link* p, int elem){
    link* temp = p;
    int i = 1;
    while(temp->next)   //因为头节点
    {
        temp = temp->next;
        if(t->elem == elem)
        {
            return i;
        }
        i++;
    }
    return -1;
}

修改

link* searchLink(link* p, int add, int elem){
    link* temp = p->next;
    for(int i = 1; i < add; i++)
    {
        temp = temp->next;
    }
    temp->elem = elem;
    return p;
}
//这里查找的只是位置,如果查找某个元素就需要两个指针一起走

循环链表实现约瑟夫环

约瑟夫环:已知 n 个人(分别用编号 1,2,3,…,n 表示)围坐在一张圆桌周围,从编号为 k 的人开始顺时针报数,数到 m 的那个人出列;他的下一个人又从 1 开始,还是顺时针开始报数,数到 m 的那个人又出列;依次重复下去,直到圆桌上剩余一个人。
例题:假设此时圆周周围有 5 个人,要求从编号为 3 的人开始顺时针数数,数到 2 的那个人出列:

代码
#include<stdio.h>
#include<stdlib.h>
typedef struct node{
    int number;
    struct node* next;
}person;
person* initLink(int n){
    person* head = (person*)malloc(sizeof(person));
    head->number = 1;
    head->next = NULL;
    person* cyclic = head;
    for(int i = 2; i <= n; i++)
    {
        person* body = (person*)malloc(sizeof(person));
        body->number = i;
        body->next = NULL;
        cyclic->next = body;
        cyclic = cyclic->next;
    }
    cyclic->next = head;
    return head;
}

void findAndKick(person* head, int k, int m){
    person* tail = head;    //尾节点
    while(tail->next != head)
    {
        tail = tail->next;
    }
    person* p = head;   //遍历
    while(p->number != k)
    {
        p = p->next;
    }
    while(p->next != p)
    {
        for(int i = 1; i < m; i++)
        {
            tail = p;   //两个一起走,感觉没必要
            p = p->next;
        }
        tail->next = p->next;   //p->next = p->next->next
        free(p);
        p = tail->next;
    }
}

双链表

代码
#include <stdio.h>
typedef struct Line{
    int data;
    struct Line* pre;
    struct Line* next;
}line;

line* initline(line* head){
    head = (line*)malloc(sizeof(line));
    head->data = 1;
    head->pre = NULL;
    head->next = NULL;
    line* list = head;
    for(int i = 2; i <= 3; i++)
    {
        line* body = (line*)malloc(sizeof(line));
        body->pre = NULL;
        body->next = NULL;
        body->data = i;

        list->next = body;
        body->pre = list;
        list = list->next;
    }
    return head;
}

line* insertLine(line* head, int data, int add){
    line* temp = (line*)malloc(sizeof(line));
    temp->data = data;
    temp->pre = NULL;
    temp->next = NULL;
    if(add == 1)
    {
        temp->next = head;
        head->pre = temp;
        head = head->pre;
    }
    else
    {
        line* body = head;
        for(int i = 1; i < add-1; i++)
        {
            body = body->next;
        }
        if(body->next == NULL)
        {
            body->next = temp;
            temp->pre = body;
        }
        else
        {
            body->next->pre = temp;
            temp->next = body->next;
            body->next = temp;
            temp->pre = body;
        }

        return head;
    }
}

line* delLine(line* head, int data)
{
    line* body = head;
    while(body != NULL)
    {
        if(body->data == data)
        {
            body->pre->next = body->next;
            body->next->pre = body->pre;
            free(body);
            return head;
        }
        body = body->next;
    }
    printf("can't find\n");
    return head;
}

int searchLine(line* head, int data)
{
    line* body = head;
    int i = 1;
    while(body != NULL)
    {
        if(body->data == data)
        {
            return i;
        }
        body = body->next;
        i++;
    }
    return -1;
}



#include <stdio.h>
#include <stdlib.h>
#include <time.h>

typedef struct line{
    int No;
    struct line* next;
}line;
typedef enum {
    false,true
}bool;
void initLine(line** head,int n){
    *head = (line*)malloc(sizeof(line));
    (*head)->next = NULL;
    (*head)->No = 1;
    line* list = *head;
    for(int i = 1; i < n; i++)
    {
        line* body = (line*)malloc(sizeof(line));
        body->next = NULL;
        body->No = i+1;
        list->next = body;
        list = list->next;
    }
    list->next = *head;
}

void display(line* head){
    line* temp = head;
    while(temp->next != head)
    {
        printf("%d",temp->No);
        temp = temp->next;
    }
}

int main(){
    line* head = NULL;
    srand((int)time(0));
    int n, shootNum, round = 1;
    printf("输入人数\n");
    scanf("%d", &n);
    initLine(&head, n);
    line* lineNext = head;
    while(head->next != head)
    {
        printf("第%d轮开始,从%d号开始\n",round,lineNext->No);
        shootNum = rand()%n + 1;
    printf("枪在第%d次会响\n", shootNum);
    line* temp = lineNext;
    for(int i = 1; i < shootNum - 1; i++)
    {
        temp = temp->next;
    }
    printf("%d 退出", temp->next->No);
    line* del = temp->next;
    temp->next = del->next;
    if(del == head)
    {
        head = head->next;
    }
    free(del);
    display(head);
    lineNext = temp->next;
    round++;
    }
    return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值