首页 > 其他分享 >p1.线性表

p1.线性表

时间:2022-11-24 20:49:18浏览次数:41  
标签:p1 线性表 int next LinkList NULL data 节点

LinearList线性表

  • 线性表的顺序表示
  • 线性表的链式表示

1.线性表

由n (n>=0)个相同类型元素组成的有序集合
	L = (a1,a2,...,ai)

线性表中元素的个数称为线性表的长度
- a1是唯一的“第一个”数据元素“,an是唯一的”最后一个“数据元素
- ai-1为ai的直接前驱,ai+1为ai的直接后继
  • 元素个数有限
  • 数据类型相同
  • 逻辑上具有顺序性

2.线性表的顺序表示

线性表顺序表示的优缺点

- 优点
	- 随机存取
	- 存储密度高
- 缺点
	- 插入&删除时需要移动大量元素
	- 线性表发生较大变化时,难以确定存储空间的容量
	- 存储分配需要连续内存空间,不够灵活

线性表的时间复杂度

  • 最好情况,表尾插入O(1)
  • 最坏情况,表头插入 O(n)
  • 平均情况,移动次数O(n/2)

线性表的时间复杂度为O(n)

3.顺序表代码

结构:

typedef int ElemType;

typedef struct SqList {
    ElemType data[MaxSize];
    int length;
} SqList;
void PrintSqList(SqList L){...}
bool InsertSqList(SqList &L, int pos, ElemType val){...}
bool DelSqList(SqList &L, int pos, ElemType &val){...}
bool GetSqList(SqList L, ElemType num, ElemType &val){...}
int main() {
    SqList L;
    L.data[0] = 1;
    L.data[1] = 2;
    L.data[2] = 3;
    L.length = 3;
    PrintSqList(L);
    //增加
    InsertSqList(L, 2, 99);
    PrintSqList(L);
    //删除
    ElemType DelValue;
    DelValue = DelSqList(L, 2, DelValue);
    PrintSqList(L);
    //查询
    ElemType GetRes;
    GetSqList(L,3,GetRes);
    printf("查询的数字是%d\n",GetRes);
    return 0;
}    

全部代码:

#include <stdio.h>

#define MaxSize 50
typedef int ElemType;

typedef struct SqList {
    ElemType data[MaxSize];
    int length;
} SqList;

void PrintSqList(SqList L) {
    for (int i = 0; i < L.length; i++) {
        printf("%3d", L.data[i]);
    }
    printf("\n");
}

bool InsertSqList(SqList &L, int pos, ElemType val) {
    if (pos < 1 || pos > L.length + 1) {
        return false;
    }
    if (L.length > MaxSize) {
        return false;
    }
    for (int j = L.length; j >= pos; j--) {
        L.data[j] = L.data[j - 1];
    }
    L.data[pos - 1] = val;
    L.length++;
    return true;
}

bool DelSqList(SqList &L, int pos, ElemType &val) {
    if (pos < 1 || pos > L.length + 1) {
        return false;
    }
    val = L.data[pos - 1];
    for (int i = pos - 1; i < L.length; i++) {
        L.data[i] = L.data[i + 1];
    }
    L.length--;
    return true;
}

bool GetSqList(SqList L, ElemType num, ElemType &val) {
    for (int i = 0;i<L.length;i++)
    {
        if(L.data[i] == num)
        {
            val = L.data[i];
            return true;
        }
    }
    val = 404;
    return false;
}

int main() {
    SqList L;
    L.data[0] = 1;
    L.data[1] = 2;
    L.data[2] = 3;
    L.length = 3;
    PrintSqList(L);
    //
    InsertSqList(L, 2, 99);
    PrintSqList(L);
    //
    ElemType DelValue;
    DelValue = DelSqList(L, 2, DelValue);
    PrintSqList(L);
    //
    ElemType GetRes;
    GetSqList(L,3,GetRes);
    printf("查询的数字是%d\n",GetRes);
    return 0;
}

4.线性表的链式表示

链式存储:逻辑上相邻的两个元素在物理位置上不相邻

与顺序表相比:

  • 插入和删除不需要移动元素,只需要修改指针
  • 不需要连续的大量内存空间
  • 单个结构体需要存储指针域
  • 不支持随机存取

以下例子为带有头节点的链表

链表节点结构体:

typedef int element_type;
typedef struct Lnode{
    element_type data; //本节点的数据
    struct Lnode *next; //下一个节点的指针
}LNode,*LinkList;
//Lnode为结构体,LinkList为结构体指针

4.1 头插法建立单链表

image

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

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

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

LinkList HeadInsert(LinkList &L) {
    LinkList newNode; //声明新节点指针
    int x;
    L = (LinkList) malloc(sizeof(LinkNode));//初始化头节点
    L->next = NULL;
    scanf("%d", &x);
    while (x != 9999) {
        newNode = (LinkList) malloc(sizeof(LinkNode));//分配新节点空间
        newNode->data = x;//新节点赋值
        newNode->next = L->next;
        L->next = newNode;
        scanf("%d",&x);
    }
}

int main() {
    LinkList testHeadInsert;
    HeadInsert(testHeadInsert);
    PrintList(testHeadInsert);
    return 0;
}

4.2 尾插法建立单链表

image

在尾插法中,需要多用一个指针(LinkList类型)来表示表尾元素的位置,

本实例中,与新节点的指针一同定义

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

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

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

LinkList TailInsert(LinkList &L)
{
    LinkList newNode,pointer; //声明新节点,以及标识表尾的指针
    int x;
    L = (LinkList) malloc(sizeof (LinkNode)); //初始化头节点
    pointer = L; //指针此时指向头节点
    scanf("%d",&x);
    while (x!=9999)
    {
        newNode = (LinkList) malloc(sizeof (LinkNode));//初始化新节点
        newNode->data = x;//新节点赋值
        newNode->next = pointer->next;
        pointer->next = newNode;
        pointer = newNode;//更新指针又重新指向新的表尾
        scanf("%d",&x);
    }
    return L;
}

int main() {
    LinkList testTailInsert;
    TailInsert(testTailInsert);
    PrintList(testTailInsert);
    return 0;
}

4.3 单链表查询(按位置)

LinkList GetElem(LinkList L, int pos) {
    int j = 1;
    LinkList p = L->next;
    if (pos == 0)
        return L;
    if(pos<1){
        return NULL;
    }
    while(p&&j<pos)
    {
        p = p->next;
        j++;
    }
    return p;
}

4.4 单链表查询(按值)

LinkList LocateElem(LinkList L,ElemType val)
{
    LinkList p = L->next;
    while (p!=NULL && p->data !=val)
    {
        p = p->next;
    }
    return p;
}

4.5 插入新节点

bool InsertElem(LinkList L,int pos,ElemType val)
{
    LinkList before = GetElem(L,pos-1); //获取新节点前一个节点的指针
    if (before == NULL)
    {
        return false;
    }
    LinkList newNode;
    newNode = (LinkList)malloc(sizeof (LinkNode));//给新节点分配空间
    newNode->data = val;
    newNode->next = before->next;
    before->next = newNode;
    return true;
}

4.6 删除节点

bool DelElem(LinkList L,int pos)
{
    LinkList before = GetElem(L,pos-1);
    printf("before=%3d\n",before->data);
    if(before == NULL)
    {
        return false;
    }
    LinkList current = before->next; //中间指针变量的创建
    before->next = current->next;
    free(current);
    current = NULL;
    return true;
}

在删除节点的过程中,需要借助一个中间指针变量current来记录此时删除的节点

因为需要通过current节点访问before节点的后继的后继

5.双向链表

双向链表结构体的定义

typedef int element_type
typedef struct DNode{
    ElemType data; //数据域
    struct DNode *prior; //前驱指针
    struct DNode *next; //后继指针
}DNode,*DinkList;

双链表的结构包括,含有空数据的头指针,以及之后的 每一个包含数据的结构体节点。

双链表的一个结构体节点占12个字节的空间

头节点(无数据的节点)只有后继指针,不成为前驱指针指向的对象

5.1双向链表头插法

image

image

DLinkList ListHeadInsert(DLinkList &DL)
{
    DNode *s;
    int x;
    DL = (DLinkList)malloc(sizeof(DNode));
    DL->next = NULL;
    DL->prior = NULL;
    scanf("%d",&x);
    while(x!=9999)
    {
        s = (DLinkList)malloc(sizeof(DNode));
        //建立新的数据节点,结构体(指针)
        s->data=x;
        s->next = DL->next;
        if(DL->next != NULL)
        {
            DL->next->prior = s;
        }
        s->prior = DL; //unnecessary own opinion
        DL->next = s;
        scanf("%d",&x);
    }
    return DL;
}

5.2 双向链表尾插法

image

DLinkList DlistTailInsert(DLinkList &DL)
{
    int x;
    DL = (DLinkList)malloc(sizeof(DNode));
    DNode *s;
    DNode *r = DL;
    DL-> = NULL;
    scanf("%d",&x);
    while(x!=9999)
    {
        s = (DNode*)malloc(sizeof(DNode));
        s->data = x;
        r->next = s;
        s->prior = r;
        r=s;
        scanf("%d",&x);
    }
    r->next = NULL;
    return DL;
}

5.3 双向链表遍历

void PrintDList(DLinkList DL)
{
    DL = DL ->next;
    while(DL != NULL)
    {
        printf("%3d",DL->data);
        DL = DL->next;
    }
    printf("\n");
}

5.4 双向链表插入元素

image

bool DListFrontInsert(DLinkList DL,int i,element_type)
{
    DLinkList p = *get_element(DL,i-1);
    if (p==NULL)
    {
        return false;
    }
    DLinkList s = (DLinkList)malloc(sizeof(DNode));
    s->data = e;
    s->next = p->next;
    p->next->prior=s;
    p->next = s;
    s->prior = p;
    return true;
}

5.5 双向链表删除元素

image

bool DListDelete(DLinkList DL,int i)
{
    DLinkList p = *get_element(DL,i-1);
    if(p==NULL)
    {
        return false;
    }
    DLinkList q;
    q = p->next;
    if (q==NULL)
    {
        return false;
    }
    p->next = q->next;
    if(q->next != NULL)
    {
        q->next->prior = p;
    }
    free(p);
    return true;
}
int main() {
    LinkList L;
//    List_HeadInsert(L);
    List_TailInsert(L);
    LNode L1;
    int res;
    L1 = *get_element(L,2);
    res = L1.data;
    printf("%d\n",res);
}

题目

41.(13分) 设线性表L=(a1,a2,a3,...,an)采用带头节点的链表保存,链表结构体定义如下

typedef struct node{
    int data;
    struct node *next;
}NODE;

请设计一个空间复杂度为O(1)并且时间上尽可能高效的算法,重新排列L中的节点,

得到线性表L'=(a1,an,a2,an-1,a3,an-1,...)

  • 给出算法设计基本思想
  • 用C/C++描述算法
  • 说明时间复杂度

设计思想

image
image

代码

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

typedef int element_type;
typedef struct Lnode {
    element_type data;
    struct Lnode *next;
} LinkNode, *LinkList;

void PrintList(LinkList L) {
    L = L->next;
    while (L != NULL) {
        printf("%3d", L->data);
        L = L->next;
    }
    printf("\n");
}

LinkList TailInsert(LinkList &L) {
    LinkList newNode, pointer;
    int x;
    L = (LinkList) malloc(sizeof(LinkNode));
    pointer = L;
    scanf("%d", &x);
    while (x != 9999) {
        newNode = (LinkList) malloc(sizeof(LinkNode));
        newNode->data = x;
        newNode->next = pointer->next;
        pointer->next = newNode;
        pointer = newNode;
        scanf("%d", &x);
    }
    return L;
}

//找链表中间节点
void FindMiddle(LinkList L, LinkList &L2) {
    L2 = (LinkList) malloc(sizeof(LinkNode));
    // 双指针法找中间节点
    LinkList pcur, ppre;
    pcur = L->next;
    ppre = L->next;
    while (pcur) {
        pcur = pcur->next;
        //pcur一次向前两个节点,要判断下一个节点是否存在
        if (pcur == NULL) {
            break;
        }
        pcur = pcur->next;
        // 偶数情况下,保证ppre不向前移动
        if (pcur == NULL) {
            break;
        }
        ppre = ppre->next;
    }
    // L2头节点指向后面一半链表
    L2->next = ppre->next;
    //前一半链表断开
    ppre->next = NULL;
}

void reverse(LinkList L2) {
    LinkList r, s, t;
    r = L2->next;
    // 第二段链表为空
    if (r == NULL) {
        return;
    }
    s = r->next;
    //链表只有一个节点
    if (s == NULL) {
        return;
    }
    t = s->next;
    while (t) {
        s->next = r;
        r = s;
        s = t;
        t = t->next;
    }
    s->next = r;
    L2->next->next = NULL;
    L2->next = s;
}

void merge(LinkList L, LinkList L2) {
    LinkList pcur, p, q;
    //pcur始终指向新链表的链表尾
    pcur = L->next;
    p = pcur->next;
    q = L2->next;
    while (p != NULL && q != NULL) {
        //先放后半段链表的一个元素
        pcur->next = q;
        pcur = pcur->next;
        q = q->next;
        //放前一段链表的一个元素
        pcur->next = p;
        pcur = pcur->next;
        p = p->next;
    }
    if (p != NULL) {
        pcur->next = p;
    }
    if (q != NULL) {
        pcur->next = q;
    }
}

int main() {
    LinkList L;
    LinkList search;
    TailInsert(L);
    PrintList(L);
    LinkList L2;
    FindMiddle(L, L2);
    printf("--------FindeMiddle--------\n");
    PrintList(L);
    PrintList(L2);
    printf("--------reverse--------\n");
    reverse(L2);
    PrintList(L);
    PrintList(L2);
    printf("--------merge--------\n");
    merge(L,L2);
    PrintList(L);
    return 0;
}

测试结果

/Users/uichuan/Documents/library/DataStructure/codes/LinkList_example01/cmake-build-debug/LinkList_example01
1 2 3 4 5 9999
  1  2  3  4  5
--------FindeMiddle--------
  1  2  3
  4  5
--------reverse--------
  1  2  3
  5  4
--------merge--------
  1  5  2  4  3

Process finished with exit code 0
/Users/uichuan/Documents/library/DataStructure/codes/LinkList_example01/cmake-build-debug/LinkList_example01
1 2 3 4 5 6 9999
  1  2  3  4  5  6
--------FindeMiddle--------
  1  2  3
  4  5  6
--------reverse--------
  1  2  3
  6  5  4
--------merge--------
  1  6  2  5  3  4

Process finished with exit code 0

时间复杂度

O(1)

标签:p1,线性表,int,next,LinkList,NULL,data,节点
From: https://www.cnblogs.com/uichuan/p/16923173.html

相关文章

  • 洛谷P1090 Java
    [NOIP2004提高组]合并果子/[USACO06NOV]FenceRepairG题目描述在一个果园里,多多已经将所有的果子打了下来,而且按果子的不同种类分成了不同的堆。多多决定把所有的......
  • 顶象首届业务安全保卫战完美落幕,快来看看TOP10里有没有你!
    今年双十一,顶象特别发起了首届业务安全保卫战,旨在召集白帽子们为业务安全贡献自己的一份力量。历经一个月,顶象首届业务安全保卫战已于20日正式落下帷幕。截止11月20日,顶......
  • 洛谷P1614 爱与愁的心痛
    爱与愁的心痛题目背景(本道题目隐藏了两首歌名,找找看哪~~~)《爱与愁的故事第一弹·heartache》第一章。《我为歌狂》当中伍思凯神曲《舞月光》居然没赢给萨顶顶,爱与愁大......
  • 洛谷P1047 [NOIP2005 普及组] 校门外的树
    [NOIP2005普及组]校门外的树题目描述某校大门外长度为l的马路上有一排树,每两棵相邻的树之间的间隔都是1米。我们可以把马路看成一个数轴,马路的一端在数轴0的位置......
  • Luogu P1453 城市环路(基环树DP)
    法一:dsu#include<bits/stdc++.h>usingll=longlong;usingnamespacestd;constintN=100010;structnode{intv,nxt;}e[N......
  • scrapy爬取后中文乱码,解决word转为html 时cp1252编码问题
    解决思路1、循环暴力寻找编码,但是不如思路3defparse(self,response):print(response.text[:100])body=response.body#直接是bytes,response.tex......
  • P1644 跳马问题 C++ 搜索回溯+dfs
    题目背景在爱与愁的故事第一弹第三章出来前先练练四道基本的回溯/搜索题吧……题目描述中国象棋半张棋盘如图1所示。马自左下角(0,0)向右上角(m,n)跳。规定只能往......
  • P1002 过河卒 详细题解 搜索回溯+递归 [NOIP2002 普及组]
    题目描述棋盘上A点有一个过河卒,需要走到目标B点。卒行走的规则:可以向下、或者向右。同时在棋盘上C点有一个对方的马,该马所在的点和所有跳跃一步可达的点称为对方......
  • Luogu P1306 斐波那契数列公约数
    斐波那契公约数题目描述对于Fibonacci数列:\[f_i=\begin{cases}[i=1]&i\leq1\\f_{i-1}+f_{i-2}&i\gt1\end{cases}\]请求出......
  • NFC芯片DP1363F兼容LRC663/RC522支持18092/15693协议多设备读卡超高性价比
    DP1363是一款高度集成的非接触读写芯片,集强大的多协议支持、最高射频输出功率、以及突破性技术低功耗卡片检测等优势一身,满足市场对更高集成度、更小外壳和互操作性的需求,......