数据结构-线性表

一、线性表的定义与特点

定义:n个数据类型相同的元素的有限集合,称为线性表。

特点:对于非空的线性表,q1存在唯一的一个第一个数据元素;q2存在唯一的一个最后一个数据元素;q3每个中间元素只有一个前驱和一个后继

二、线性表的顺序表示

线性表的顺序表示又称作顺序表,对于连续的一串数据,采用在内存单元上一串连续的内存进行储存。

顺序表基于数组进行数据储存。

cpp 复制代码
#include <stdio.h>
#include <stdlib.h>

//顺序表的初始化
#define MAX 100
typedef int elemtype;
typedef struct 
{
    elemtype data[MAX];//seqlist第一个元素是数组,第二个是一个整数
    int length;
}seqlist;
void initlist(seqlist *L)//顺序表的初始化
{
    L->length=0;
}
int main(int argc, char const *argv[])
{
    seqlist list;
    initlist(&list);

    return 0;
}
//*************************************************************
//顺序表在尾部插入元素
int appendelem(seqlist *L,elemtype e)//参数一是指向seqlist类型结构体的指针,参数二是要写入的数据
{
    if(L->length>=MAX)
    {
        printf("顺序表已满\n");
    }
    else
    {
        L->data[L->length]=e;
        L->length++;
    }
}
//*************************************************************
//遍历
void listElem(seqlist *L)
{
    for (int i = 0; i < L->length; i++)
    {
        printf("%d ", L->data[i]);
    }
    printf("\n");
}
//*************************************************************
//顺序表插入元素
int insertelem(seqlist *L,int pos,elemtype e)
{
    if(pos<=L->length)
    {//因为后面是空位可以直接进行替换,不用创造中间变量,从后遍历数组更佳
        for(int i=L->length-1;i>=pos-1;i--)//第一个减一是为了进行移位,第二个减一是因为位置与数组下标错一位
        {
            L->data[i+1]=L->data[i];
        }//循环后空出需要添加元素的位置
        L->data[pos-1]=e;
        L->length++;
    }
}
//顺序表的插入时间复杂度为O(n)。
//*************************************************************
//顺序表删除元素
int deletelem(seqlist *L,int pos,elemtype e)//e的作用见下
{
    if(pos<=L->length)
    {//因为后面是空位可以直接进行替换,不用创造中间变量,从后遍历数组更佳
        for(int i=pos;i>=L->length;i++)//第一个减一是为了进行移位,第二个减一是因为位置与数组下标错一位
        {
            L->data[i-1]=L->data[i];
        }//循环后空出需要添加元素的位置
        L->length--;
    }
}
/* 在理解中,删除元素后,后面的元素占据了数组中前一个元素的位置,
但是实际上,在内存空间中,元素的绝对储存位置并没有发生变化,
也就是说删除元素依然在原来的内存地址中储存着,可以通过一个
指向这个地址的指针来调取这个删除元素(elemtype deldata;&deldata; *e=L->data[pos-1]) 
对数组来说,修改后的length后一位上储存的还是之前的最后一位的值,因为数组的内存空间是连续的*/
//*************************************************************
// 查找
int findElem(seqlist *L, elemtype e)
{
    for (int i = 0; i < L->length; i++)
    {
        if (L->data[i] == e)
        {
            return i + 1;//返回位置值,是下标加1
        }
    }
    return 0;
}
//*************************************************************
//顺序表的动态分配内存地址初始化(使用堆内存)
typedef struct 
{
    int *data;
    int length;
}seqlist;
seqlist* initlist()//堆内存的初始化函数
{
    seqlist *L=(seqlist*)malloc(sizeof(seqlist));//将结构体创建在堆内存中
    L->data=(int*)malloc(sizeof(int)*100);//将data数组创建在堆内存中
    L->length=0;
    return L; //L指向堆内存中的顺序表
}
//之后可以定义seqlist *list =initlist();
/* 此时的list不再是上面的一个结构体变量,而是一个结构体指针,
也就是说上面编写的函数再一次使用时不需要对list取地址了。 */

三、线性表的链式表示(链表)

链表可以表示一个数据元素与后继元素的逻辑关系,为此,一个元素要储存其本身的信息和一个指向后继的信息,这两个部分的组合叫做一个节点。

节点含有两个域:数据域,存储数据元素信息:指针域,存储后继的储存位置,这个信息叫做指针或者链。

多个节点的链接构成一个链表。

链表的一个节点用结构体来表示。

(一)单链表

cpp 复制代码
//单链表
typedef int ElemType;
typedef struct node
{
    ElemType data;
    struct node *next;
}Node;
//初始化链表
Node* initList()
{
    Node *head=(Node*)malloc(sizeof(Node));
    head->data=-1;
    head->next=NULL;
    return head;
}
//头插法
int insertHead(Node*L,ElemType e)
{
    Node *p=(Node*)malloc(sizeof(Node));
    p->data=e;
    p->next=L->next;
    L->next=p;
    return 1;
}
//遍历
void listNode(Node*L)
{
    Node *p = L->next; // 从第一个有效节点开始
    while(p!=NULL)
    {
        printf("%d\n",p->data);
        p=p->next;
    }
    printf("\n");
}
//获取尾部节点
Node* get_tail(Node*L)
{
    Node *p = L; // 从链表头开始遍历
    while (p->next!=NULL)
    {
        p=p->next;
    }
    return p;
}
//尾插法
Node* insertTail(Node *tail,ElemType e)
{
    Node *p=(Node*)malloc(sizeof(Node));
    p->data=e;
    tail->next = p; // 核心:将新节点链接到原尾节点后
    p->next=NULL; 
    return p;
}
//指定位置插入
int insertNode(Node *L,int pos,ElemType e)
{
    Node *p=L;//保存前驱节点
    int i=0;
    while (i<pos-1)
    {
        i++;
        p=p->next;
        if (p==NULL)
        {
            return 0;
        }
    } 
    //需要插入的新节点
    Node *q=(Node*)malloc(sizeof(Node));
    q->data=e;
    q->next=p->next;
    p->next=q;
    return 1;
}
//指定位置删除
int deleteNode(Node *L,int pos)
{
    Node *p=L;//要删除的指针前驱
    int i;
    while (i<pos-1)
    {
        i++;
        p=p->next;
        if (p==NULL)
        {
            return 0;
        }
    }
    Node *q=p->next;
    p->next=q->next;
    free(q);
    return 1;
}
//获取链表长度
int listlength(Node *L)
{
    Node *p=L->next;
    int len=0;
    while (p!=NULL)
    {
        p=p->next;
        len++;
    }
    return len;
}
//释放链表
void freeList(Node *L)
{
    Node *p=L->next;
    Node *q;
    while (p!=NULL)
    {
        q=p->next;
        free(p);
        p=q;
    }
    L->next=NULL; 
}
//主函数 
int main(int argc, char const *argv[])
{
    Node *list=initList();
    Node *tail=get_tail(list);
    tail=insertTail(tail,10);
    tail=insertTail(tail,20);
    tail=insertTail(tail,30);
    listNode(list);
    insertNode(list,2,15);
    listNode(list);
    deleteNode(list,2);
    listNode(list);
    printf("%d\n",listlength(list));
    return 0;
}

(二)循环链表

cpp 复制代码
//*************************************************************
//单向循环链表
//判断是否成环
int isCycle(Node *head)//原理:不成环的链表的两个不同速度的指针不可能相遇
{
    Node *fast=head;
    Node *slow=head;
    while (fast!=NULL&&fast->next!=NULL)
    {
        fast=fast->next->next;
        slow=slow->next;
        if (fast==slow)
        {
            return 1;
        }
    }
    return 0;
}
//寻找环的入口
//原理:先判断环的节点数,让快指针先走这个数字,然后两个指针同时走,相遇的地址就是环的入口
Node* findBegin(Node *head)
{
    Node *fast=head;
    Node *slow=head;
    while (fast!=NULL&&fast->next!=NULL)
    {
        fast=fast->next->next;
        slow=slow->next;
        if (fast==slow)//说明存在环
        {
            int count=1;
            while (fast->next!=slow)//判断节点个数
            {
                count++;
                fast=fast->next;
            }
            fast=head;//两个指针回到头结点
            slow=head;
            for (int i = 0; i < count; i++)//让快指针先走count步数
            {
                fast=fast->next;
            }
            while (fast!=slow)//两个指针同时行动,直到相遇
            {
                fast=fast->next;
                slow=slow->next;
            }
            return slow;//返回入环点
        }
    }
    return NULL;
}

(三)双向链表

cpp 复制代码
//双向链表
 typedef int Elemtype;
typedef struct node
{
    Elemtype data;
    struct node *next,*prev;
}Node;

//初始化链表
Node* initList()
{
    Node *head=(Node*)malloc(sizeof(Node));
    head->data=-1;
    head->next=NULL;
    head->prev=NULL;
    return head;
}
//头插法
int insertHead(Node *L,Elemtype e)
{
    Node *p=(Node*)malloc(sizeof(Node));
    p->data=e;
    p->prev=L;
    p->next=L->next;
    if (L->next!=NULL)
    {
        L->next->prev=p;
    }
    L->next=p;
    return 1;
    
}
//遍历
void listNode(Node*L)
{
    Node *p = L->next; // 从第一个有效节点开始
    while(p!=NULL)
    {
        printf("%d\n",p->data);
        p=p->next;
    }
    printf("\n");
}
//尾插法
int insertTail(Node *L,Elemtype e)
{
    Node *p=(Node*)malloc(sizeof(Node));
    p->data = e;
    p->next = NULL;       // 新节点作为尾节点,next指向NULL
    p->prev = L;       // 新节点的前驱指向原尾节点
    L->next = p;       // 原尾节点的后继指向新节点
    return 1;
}
//在指定位置插入数据
int insertNode(Node *L,int pos,ElemType e)
{
    Node *p=L;//保存前驱节点
    int i=0;
    while (i<pos-1)
    {
        i++;
        p=p->next;
        if (p==NULL)
        {
            return 0;
        }
    } 
    //需要插入的新节点
    Node *q = (Node*)malloc(sizeof(Node));
    q->data = e;
    q->prev = p;
    q->next = p->next;
    p->next->prev = q;
    p->next = q;
    return 1;
} 
//指定位置删除
int deleteNode(Node *L,int pos)
{
    Node *p=L;//要删除的指针前驱
    int i;
    while (i<pos-1)
    {
        i++;
        p=p->next;
        if (p==NULL)
        {
            return 0;
        }
    }
    Node *q=p->next;
    p->next=q->next;
    q->next->prev=p;
    free(q);
    return 1;
}
int main(int argc, char const *argv[])
{
    Node *list=initList();
    insertHead(list,10);
    insertHead(list,20);
    insertTail(list,30);
    insertTail(list,0);
    insertTail(list,60);
    listNode(list);
    return 0;
}

四、线性表的应用

cpp 复制代码
//例题,寻找链表倒数某一位上的值,解法:快慢双指针
typedef int ElemType;
typedef struct node
{
    ElemType data;
    struct node *next;
}Node;
//初始化链表
Node* initList()
{
    Node *head=(Node*)malloc(sizeof(Node));
    head->data=-1;
    head->next=NULL;
    return head;
}
int findNodeFS(Node *L,int k)
{
    Node *fast=L->next;
    Node *slow=L->next;
    for(i=0;i<k;i++)
    {
        fast=fast->next;
    }
    while (fast!=NULL)
    {
        fast=fast->next;
        slow=slow->next;
    }
    printf("%d   %d\n",k,slow->data);
    return 1;
}
cpp 复制代码
//两个字符串结尾共用相同,寻找相同首字母
Node* findIntersectionNode(Node *headA,Node *headB)
{
    if (headA==NULL||headB==NULL)//判断是否存在空链表
    {
        return NULL;
    }
    int lenA=0,lenB=0;//分别遍历AB链表,获得两个链表的长度
    Node *p=headA;
    while (p!=NULL)
    {
        p=p->next;
        lenA++;
    }
    p=headB;
    while (p!=NULL)
    {
        p=p->next;
        lenB++;
    }
    Node *f;
    Node *s;
    int step;
    step=fabs(lenA-lenB);//计算两个链表长度的差值
    if (lenA>lenB)//选择快慢指针
    {
        f=headA;
        s=headB;
    }
    else
    {
        s=headA;
        f=headB;
    }
    for (int i = 0; i < step; i++)//让快指针先走step步
    {
        f=f->next;
    }
    while (f!=s)//两个指针同时走,直到找到相同地址
    {
        f=f->next;
        s=s->next;
    }
    return f;//返回地址
}
相关推荐
tobias.b2 小时前
408真题解析-2010-1-数据结构-栈基础操作
数据结构·408真题解析
菜鸟233号2 小时前
力扣213 打家劫舍II java实现
java·数据结构·算法·leetcode
方便面不加香菜2 小时前
数据结构--栈和队列
c语言·数据结构
Pluchon2 小时前
硅基计划4.0 算法 动态规划进阶
java·数据结构·算法·动态规划
2401_841495645 小时前
【Python高级编程】单词统计与查找分析工具
数据结构·python·算法·gui·排序·单词统计·查找
-To be number.wan5 小时前
【数据结构真题解析】哈希表高级挑战:懒惰删除、探测链断裂与查找正确性陷阱
数据结构·算法·哈希算法
Qhumaing5 小时前
数据结构——例子求算法时间复杂度&&空间复杂度
数据结构·算法
鱼跃鹰飞6 小时前
Leetcode1027:最长等差数列
java·数据结构·算法
Stardep6 小时前
算法入门20——二分查找算法——搜索插入位置
数据结构·算法·leetcode