线性表就是由n(n>=0)个相同类型的数据元素组成的有限序列,他是最基本,最常用的一种线性结构。理解为一条线,有唯一的开始与结束。除了第一个元素每个元素都有唯一的直接前驱;除了最后一个元素,每个元素都有唯一的直接后继。
线性表有两种存储方式:顺序存储和链式存储。采用顺序存储的被称为顺序表。采用链式存储的被称为链表。

一:顺序表

顺序表就是说相邻的数据在计算机内部存储位置也是相邻的。顺序存储方式中元素存储是连续的,中间不允许有空,可以快速定位某个元素,所以插入删除时要移动大量元素。根据空间分配方法的不同,顺序表可分为静态存储和动态分配两种。

静态分配:

就是指定长数组需要预先分配一段固定大小的连续空间,但易造成的问题就是操作过程中超出预支空间,产生溢出。

  1. #define Maxsize 100
  2. typedef struct{
  3. Elem_Type date[Maxsize];
  4. int length;
  5. }Sqlist;

动态分配

为解决可能产生的溢出问题,我们用动态分配方法,也就是用基地址(数组首地址)来记录,一旦溢出就开辟更大空间来替换掉原来的存储空间,从而达到扩容的目的。

#define Maxsize 100

typedef struct{
    Elem_Type *elem;
    int length;
}Sqlist;

1.插入

想要在顺序表中第i个位置之前插入一个元素e,需要从最后一个元素开始,后移一位,直到空出i位置。

算法步骤:
(1)判断插入位置,位置必须大于1小于最后一位加一。
(2):判断存储空间是否已满。
(3):依次后移
(4):插入新元素
(5):表长加一,插入完成后返回TRUE。

bool ListInsert(sqlist &l,int i,int e)
{
    if(i<1||i>L.length+1)
        return false;
    if(L.length == Maxsize)
        return false;
    for(int j = L.length-1;j>=i-1;j--)
        {
            L.elem[j+1]=L.elem[j];
        }
    L.elem[i-1]=e;
    L.length++;
    return true;
}

2.删除

在顺序表中删除第i个元素时,需将该变量暂存到变量e中,之后从i+1一个个前移,直至第n个元素。

算法步骤:
(1):判断插入位置i是否合法(1<=i<=L.length)
(2):将欲删除元素保存在e中。
(3): 将i+1至n的元素依次前移1位
(4):表长-1,删除完毕返回TRUE

bool ListDelete(sqlist &L,int i,int &e)
{
    if(i<1||i>L.length-1)
        return false;
    e = L.elem[i-1];
    for(int j=i;j<=L.length-1;j++)
    {
        L.elem[j-1]=L.elem[j];
    }
    L.length--;
    return true;
}

二:单链表

小程序:

为了方便理解,此处以一个小程序示例,如出现乱码请将编码设置为GBK,以此来理解单链表中功能的实现。本次我们将以讲解单向链表为主,其他链表就是稍加修改的单向链表。

#include<iostream>
#include<cstdlib>
using namespace std;

typedef struct Lnode{
    int data; //结点的数据域
    struct Lnode *next; //结点的指针域
}Lnode,*LinkList; //LinkList为指向结构体LNode的指针类型

bool InitList_L(LinkList &L){//构造一个空的单链表L
    L=new Lnode;     //生成新结点作为头结点,用头指针L指向头结点
    if(!L)
        return false;  //生成结点失败
    L->next=NULL;   //头结点的指针域置空
    return true;
}

void CreateList_H(LinkList &L){//前插法创建单链表
    //输入n个元素的值,建立到头结点的单链表L
    int n;
    LinkList s; //定义一个指针变量
    L=new Lnode;
    L->next=NULL; //先建立一个带头结点的空链表
    cout<<"请输入元素个数n:"<<endl;
    cin>>n;
    cout<<"请依次输入n个元素:"<<endl;
    cout<<"前插法创建单链表..."<<endl;
    while(n--){
        s=new Lnode; //生成新结点s
        cin>>s->data; //输入元素值赋给新结点的数据域
        s->next=L->next;
        L->next=s; //将新结点s插入到头结点之后
    }
}

void CreateList_R(LinkList &L){//尾插法创建单链表
    //输入n个元素的值,建立带表头结点的单链表L
    int n;
    LinkList s, r;
    L=new Lnode;
    L->next=NULL; //先建立一个带头结点的空链表
    r=L; //尾指针r指向头结点
    cout<<"请输入元素个数n:"<<endl;
    cin>>n;
    cout<<"请依次输入n个元素:"<<endl;
    cout<<"尾插法创建单链表..."<<endl;
    while(n--){
        s=new Lnode;//生成新结点
        cin>>s->data; //输入元素值赋给新结点的数据域
        s->next=NULL;
        r->next=s;//将新结点s插入尾结点*r之后
        r=s;//r指向新的尾结点s
    }
}

bool GetElem_L(LinkList L, int i, int &e){//单链表的取值
    //在带头结点的单链表L中查找第i个元素
    //用e记录L中第i个数据元素的值
    int j;
    LinkList p;
    p=L->next;//p指向第一个结点,
    j=1; //j为计数器
    while(j<i&&p){ //顺链域向后扫描,直到p指向第i个元素或p为空
        p=p->next; //p指向下一个结点
        j++; //计数器j相应加1
    }
    if(!p||j>i)
        return false; //i值不合法i>n或i<=0
    e=p->data; //取第i个结点的数据域
    return true;
}

bool LocateElem_L(LinkList L,int e){ //按值查找
    //在带头结点的单链表L中查找值为e的元素
    LinkList p;
    p=L->next;
    while(p&&p->data!=e)//顺链域向后扫描,直到p为空或p所指结点的数据域等于e
        p=p->next; //p指向下一个结点
    if(!p)
        return false; //查找失败p为NULL
    return true;
}

bool ListInsert_L(LinkList &L,int i,int e){//单链表的插入
    //在带头结点的单链表L中第i个位置插入值为e的新结点
    int j;
    LinkList p,s;
    p=L;
    j=0;
    while(p&&j<i-1){ //查找第i-1个结点,p指向该结点
        p=p->next;
        j++;
    }
    if(!p||j>i-1)//i>n+1或者i<1
        return false;
    s=new Lnode;     //生成新结点
    s->data=e;       //将新结点的数据域置为e
    s->next=p->next; //将新结点的指针域指向结点ai
    p->next=s;       //将结点p的指针域指向结点s
    return true;
}

bool ListDelete_L(LinkList &L, int i){ //单链表的删除
    //在带头结点的单链表L中,删除第i个位置
    LinkList p, q;
    int j;
    p=L;
    j=0;
    while((p->next)&&(j<i-1)){ //查找第i-1个结点,p指向该结点
        p=p->next;
        j++;
    }
    if(!(p->next)||(j>i-1))//当i>n或i<1时,删除位置不合理
        return false;
    q=p->next;        //临时保存被删结点的地址以备释放空间
    p->next=q->next; //改变删除结点前驱结点的指针域
    delete q;        //释放被删除结点的空间
    return true;
}

void Listprint_L(LinkList L){ //单链表的输出
    LinkList p;
    p=L->next;
    while(p){
        cout<<p->data<<"\t";
        p=p->next;
    }
    cout<<endl;
}

int main(){
    int i,x,e,choose;
    LinkList L;
    cout<<"1. 初始化\n";
    cout<<"2. 创建单链表(前插法)\n";
    cout<<"3. 创建单链表(尾插法)\n";
    cout<<"4. 取值\n";
    cout<<"5. 查找\n";
    cout<<"6. 插入\n";
    cout<<"7. 删除\n";
    cout<<"8. 输出\n";
    cout<<"0. 退出\n";
    choose=-1;
    while(choose!=0){
        cout<<"请输入数字选择:";
        cin>>choose;
        switch(choose){
        case 1: //初始化一个空的单链表
            if(InitList_L(L))
                cout<<"初始化一个空的单链表!\n";
            break;
        case 2: //创建单链表(前插法)
            CreateList_H(L);
            cout<<"前插法创建单链表输出结果:\n";
            Listprint_L(L);
            break;
        case 3: //创建单链表(尾插法)
            CreateList_R(L);
            cout<<"尾插法创建单链表输出结果:\n";
            Listprint_L(L);
            break;
        case 4: //单链表的按序号取值
            cout<<"请输入一个位置i用来取值:";
            cin>>i;
            if (GetElem_L(L,i,e)){
                cout<<"查找成功\n";
                cout<<"第"<<i<<"个元素是:"<<e<<endl;
            }
            else
                cout<<"查找失败\n\n";
            break;
        case 5: //单链表的按值查找
            cout<<"请输入所要查找元素x:";
            cin>>x;
            if(LocateElem_L(L,x))
                cout<<"查找成功\n";
            else
                cout<<"查找失败! "<<endl;
            break;
        case 6: //单链表的插入
            cout<<"请输入插入的位置和元素(用空格隔开):";
            cin>>i;
            cin>>x;
            if(ListInsert_L(L,i,x))
                cout<<"插入成功.\n\n";
            else
                cout<<"插入失败!\n\n";
            break;
        case 7: //单链表的删除
            cout<<"请输入所要删除的元素位置i:";
            cin>>i;
            if(ListDelete_L(L,i))
                cout<<"删除成功!\n";
            else
                cout<<"删除失败!\n";
            break;
        case 8: //单链表的输出
            cout<<"当前单链表的数据元素分别为:\n";
            Listprint_L(L);
            cout<<endl;
            break;
        }
    }
    system("pause");
    return 0;
}

图片.png
链表就是线性表的链式存储方式,逻辑上相邻的数据在计算机内的存储位置不一定相邻。为表示相邻关系,需要给每个元素都附加一个指针域,指向下一个元素的存储位置。
每个节点都包含两个域:数据域和指针域。数据域存储数据元素,指针域存储下一个节点的地址,因此指针指向的类型也是节点类型。链表中的每个指针都指向下一个节点,也朝向同一方向,这样的链表被称为单向链表或者单链表。

typedef struct Lnode{
Elem Type data;
    struct Lnode *next;
}Lnode,*Linklist;

定义了上面的节点结构体之后,就可以通过它将若干节点连接在一起,形成一个单链表了。
显然,不论这个链表多长,只要找到他的头,就可以拉起整个单链表。而有时为了方便操作,还会给单链表增加一个不存放数据的头结点(也可以用来存储下表长),给单链表加上头结点,可以理解成给一串钥匙加了个钥匙扣。

随机存取与顺序存取:

若想在顺序表中找第i个元素,则可以立即通过L.elem[i-1]找到,想找哪个就找那个,这种存取被称作随机存取。但放到链表中,是无法这样操作的,只能从头一个个读取,直至找到想要的元素。这种被称作顺序存取。

1.插入

在第i个节点之前插入元素e,就相当于在i-1节点后插入,则插入操作如视频图解。

2.删除

删除某个节点,实际上就是跳过去,图解如视频。

三:双向链表

在单链表中,每个元素附加一个指针域,而双向链表就是添加两个指针域,一个指向前驱节点,一个指向后继节点。其他同单向链表。

四:循环链表(环)

单向链表存在问题,也就是从当前节点开始,无法访问前面的节点。所谓循环链表就是最后一个节点后继指向头结点。

五:静态链表

链表的另一种表示方法就是,可以用一个数组存储数据,另一个数组记录当前数据后继的下标。
例如用一个静态链表来表示一个动态单向循环链表,就可以先把数据存储在一维数组data【】中,用后继数组right【】记录每个元素的后继下标。

练习:
uva101:http://web.kshs.kh.edu.tw/academy/luckycat/homework/q101.htm

#include<bits/stdc++.h>
using namespace std;

vector<int>block[30];
int n;

void init()
{
    cin>>n;
    for(int i=0;i<n;i++)
        block[i].push_back(i);
}

void loc(int x,int &p,int &h)//找位置
{
    for(int i=0;i<n;i++)
        for(int j=0;j<block[i].size();j++)
        {
            if(block[i][j]==x)
            {
                p=i;
                h=j;
            }
        }
}

void goback(int p,int h)//p堆>h的所有块归位
{
    for(int i=h+1;i<block[p].size();i++)
    {
        int k=block[p][i];
        block[k].push_back(k);
    }
    block[p].resize(h+1);//重置大小
}

void moveall(int p,int h,int q)//p堆>=h的所有块移动到q之上
{
    for(int i=h;i<block[p].size();i++)
    {
        int k=block[p][i];
        block[q].push_back(k);
    }
    block[p].resize(h);//重置大小
}

void solve()
{
    int a,b;
    string s1,s2;
    while(cin>>s1)
    {
        if(s1=="quit")
            break;
        cin>>a>>s2>>b;
        int ap=0,ah=0,bp=0,bh=0;
        loc(a,ap,ah);
        loc(b,bp,bh);
        if(ap==bp)
            continue;
        if(s1=="move")//a归位
            goback(ap,ah);
        if(s2=="onto")//b归位
            goback(bp,bh);
        moveall(ap,ah,bp);
    }
}

void print()
{
    for(int i=0;i<n;i++)
    {
        cout<<i<<":";
        for(int j=0;j<block[i].size();j++)
            cout<<" "<<block[i][j];
        cout<<endl;
    }
}

int main()
{
    init();
    solve();
    print();
    return 0;
}

uva11988:http://web.kshs.kh.edu.tw/academy/luckycat/homework/q11988.htm

#include<bits/stdc++.h>
using namespace std;

void solve(string s)
{
    int len=s.length();
    list<char> text;
    list<char>::iterator it=text.begin();
    for(int i=0;i<len;i++)
    {
        if(s[i]=='[')
            it=text.begin();
        else if(s[i]==']')
                it=text.end();
            else
            {
                it=text.insert(it,s[i]);
                it++;
            }        
    }
    for(it=text.begin();it!=text.end();it++)
        cout<<*it;
    s.clear();
    cout<<endl;
 } 

int main()
{
    string s;
    while(cin>>s)
        solve(s);
    return 0;
 }

uva12657:https://vjudge.net/problem/UVA-12657

#include<bits/stdc++.h>
using namespace std;

int r[100000+5],l[100000+5];

void init(int n) 
{
    for(int i=1;i<=n;i++)
    {
        l[i]=i-1;
        r[i]=(i+1)%(n+1);
    }
    r[0]=1;
    l[0]=n;
}

void link(int L,int R) 
{
    r[L]=R;
    l[R]=L;
}

int main()
{
    int n,m,a,x,y,k=0;
    bool flag;         
    while(cin>>n>>m)
    {
        flag=false;
        init(n);
        for(int i=0;i<m;i++)
        {
            cin>>a;
            if(a==4)
                flag=!flag;//翻转 
            else
            {
                cin>>x>>y;
                if(a==3&&r[y]==x) swap(x,y);
                if(a!=3&&flag)
                    a=3-a;
                if(a==1&&x==l[y])
                    continue;
                if(a==2&&x==r[y])
                    continue;
                int Lx=l[x],Rx=r[x],Ly=l[y],Ry=r[y];
                if(a==1)
                {
                    link(Lx,Rx);//删除x 
                    link(Ly,x);
                    link(x,y);//x插入y左 
                }
                else if(a==2)
                    {
                        link(Lx,Rx);//删除x 
                        link(y,x);
                        link(x,Ry);//x插入y右
                    }
                    else if(a==3)
                    {
                        if(r[x]==y)
                        {
                            link(Lx,y);
                            link(y,x);
                            link(x,Ry);
                        }
                        else
                        {
                            link(Lx,y);//交换位置 
                            link(y,Rx);
                            link(Ly,x);
                            link(x,Ry);
                        }
                    }
            }    
        }
        int t=0;
        long long sum=0;
        for(int i=1;i<=n;i++)
        {
            t=r[t];
            if(i%2==1)
                sum+=t;
         }
        if(flag&&n%2==0)
            sum=(long long)n*(n+1)/2-sum;
        cout<<"Case "<<++k<<": "<<sum<<endl;
    }
    return 0;
 }