数据结构 第一篇 链表

引言

最大子序列和

整数序列A1, A2,… An (可能有负数),求A1~An的一个子序列Ai~Aj,使得Ai到Aj的和最大

注意:序列是有顺序的,不是把所有正数相加就可以的

#include <iostream>

using namespace std;

int Data[]={-2, 11, -4, 13, -5, 2, -5, -3, 12, -9};

int MaxSubSum(int *pData,int nLen)
{
    int nTmp=0;
    int nSum=0;
    for(int i=0;i<nLen;i++)//序列头a[i]
        for(int j=i;j<nLen;j++)//序列尾a[j]
        {
            nSum=0;
            for(int k=i;k<j;k++)
                nSum+=pData[k];//a[i]~a[j]的和
            if(nSum>nTmp)
                nTmp=nSum;
        }
    return nTmp;
}

int main(int argc, char *argv[])
{
    cout << MaxSubSum(Data,sizeof(Data)/sizeof(int)) << endl;
    return 0;
}
//运行时间为O(N^3)

其他方式

线性结构

线性表

线性表是一个含有n≥0个结点的有限序列,对于其中的结点,有且仅有一个开始结点没有前驱但有一个后继结点,有且仅有一个终端结点没有后继但有一个前驱结点,其它的结点都有且仅有一个前驱和一个后继结点。

线性表基本操作

  • 初始化:malloc

    • 销毁:free
    • 置空:len
    • 判断是否空:len
    • 获取长度:len
    • 根据位置查找元素
    • 根据元素查找位置
    • 获取指定元素的前驱和后继
    • 插入元素
    • 删除元素
    • 遍历

线性表实现

顺序线性表

  • 特点

    • 顺序线性表的物理存储地址连续
    • 随机读取效率高
  • 实现

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

#define OK              1
#define ERROR           0
#define INIT_SIZE       10
#define INCREAMENT_SIZE 5

typedef int Sta;
typedef int EType;

typedef struct
{
    EType *e;
    int len;//当前长度
    int size;//分配的表长度
}SqList;

Sta InitList(SqList *L)
{
    L->e=(EType*)malloc(INIT_SIZE*sizeof(EType));
    if(!L->e)
        return ERROR;
    L->len=0;
    L->size=INIT_SIZE;
    return OK;
}

//销毁操作,把表的整个结构给销毁掉,把原来所占有的内存空间都给释放出来
Sta DestroyList(SqList *L)
{
    if(L->e)
        free(L->e);
    L->len=0;
    L->size=0;
    return OK;
}

//清空操作,把表中的元素清空,但表结构依然存在于内存中
Sta ClearList(SqList *L)
{
    L->len=0;
    return OK;
}

Sta IsEmpty(const SqList* L)
{
    return (bool)(L->len);
}

Sta GetLen(const SqList *L)
{
    return L->len;
}

Sta GetElem(const SqList* L,int i,EType *e)
{
    if(i<1 || i>L->len)
        return ERROR;
    *e=L->e[i-1];
    return OK;
}

//返回位置1为起点,非偏移量
Sta FindElem(const SqList *L,EType e,int *pos)
{
    int i=0;
    *pos = 0;
    for(i=0;i < L->len;i++)
    {
        if(*(L->e) == e)
            *pos = i+1;
    }
    if(0 == *pos)
        return ERROR;
    return OK;
}

//插入位置1为起点,非偏移量
Sta InsertElem(SqList *L,int i,EType e)
{
    EType *elem;
    if(i<1 || i>L->len+1)
        return ERROR;
    //判断容量 容量不够先扩容
    if(L->len >= L->size)
    {
        elem = (EType*)realloc(L->e,(L->size+INCREAMENT_SIZE)*sizeof(EType));
        if(!elem)
            return ERROR;
        L->e = elem;
        L->size += INCREAMENT_SIZE;
    }
    //扩容后再插入数据
    EType *p = &L->e[i-1];//要插入位置的前一个元素的地址
    EType *q = &L->e[L->len-1];//列表最后一个元素的地址

    //整体向右移动1位
    for(;q>=p;q--)
        *(q+1)=*q;
    *p=e;
    ++L->len;
    return OK;
}

Sta DeleteElem(SqList* L,int i,EType *e)
{
    if(i<1 || i>L->len)
        return ERROR;
    EType *p = &L->e[i-1];
    *e = *p;

    //整体向左移动1位
    for(;p<&L->e[L->len-1];p++)
        *p = *(p+1);
    --L->len;
    return OK;
}

void Visit(EType e)
{
    printf("%d ",e);
}

Sta TraverseList(const SqList L,void(*visit)(EType))
{
    int i;
    for(i=0;i<L.len;i++)
        visit(L.e[i]);
    return OK;
}

int main()
{
    SqList L;
    if(InitList(&L))
    {
        for(int i=0;i<10;i++)
            InsertElem(&L,i,i);
        TraverseList(L,Visit);

        int elem;
        DeleteElem(&L,9,&elem);
        Visit(elem);
        TraverseList(L,Visit);

        GetElem(&L,8,&elem);
        Visit(elem);
        TraverseList(L,Visit);
    }
    return 0;
}
//注意:size和length的区别

链式线性表

  • 特点

    • 链式线性表的物理存储地址不连续
    • 插入删除效率高
  • 实现

    • 单链表:头结点无前一个节点 尾结点无后一个结点

    这里写图片描述

    • 循环链表:尾结点指向头结点
    • 双向链表:相邻结点间双向指向,头结点与尾结点双向指向
//单链表
#include <stdio.h>
#include <stdlib.h>
#include <iostream>

#define OK              1
#define ERROR           0

typedef int EType;
typedef int Sta;

typedef struct Node
{
    EType data;
    struct Node *next;
}*LinkList;

//把各个函数的入口设为head名比L名更好,因为传入链表的时候传入的是头指针

//初始化只创建头结点 头结点next指针指向为NULL
//这里传入的必须是头结点的指针 否则这和函数传入一个临时变量一样!
Sta InitList(LinkList* head)
{
    *head = (LinkList)malloc(sizeof(Node));
    if(!head)
        return ERROR;
    (*head)->next = NULL;
    return OK;
}

//销毁是把链表这个结构的内存都释放了
void DestroyList(LinkList *head)
{
    LinkList p = (*head)->next;
    LinkList q;
    while(p)//终止条件为结点为NULL
    {
        q = p;
        p = p->next;
        free(q);//释放当前结点内存时,先临时保存当前结点指向下一结点的指针
    }
    free(*head);//释放头结点
}

//清空是把链表中的元素清空,但链表还存在
//头结点指向为NULL 销毁头结点以后的结点
void ClearList(LinkList head)
{
    LinkList p = head->next;
    head->next = NULL;
    DestroyList(&p);
}

bool IsEmpty(LinkList head)
{
    if(head->next)
        return false;
    else
        return true;
}

int GetLength(LinkList head)
{
    int i=0;
    LinkList p = head->next;
    while(p)
    {
        i++;
        p = p->next;
    }
    return i;
}

Sta GetElem(LinkList head,int i,EType *e)
{
    int j = 1;
    LinkList p = head->next;
    while(p && j<i)
    {
        j++;
        p = p->next;
    }

    if(!p || j>i)
        return ERROR;
    *e = p->data;
    return OK;
}

Sta FindElem(LinkList head,EType e,int *pos)
{
    int i = 0;
    LinkList p = head->next;
    while(p)
    {
        i++;
        if(e == p->data)
        {
            *pos = i;
            return OK;
        }
        p = p->next;
    }
    return ERROR;
}

//malloc一个结点,找到要插入的结点位置,然后插入该结点
Sta InsertElem(LinkList head,int i,EType e)
{
    int j = 0;
    LinkList p = head;
    LinkList s = (LinkList)malloc(sizeof(Node));;
    while(p && j<i-1)
    {
        j++;
        p = p->next;
    }
    if(!p || j>i-1)
        return ERROR;

    s->next = p->next;
    p->next = s;

    s->data = e;

    return OK;
}

//找到要删除位置的结点,然后删除该结点,free掉内存
Sta DeleteElem(LinkList head,int i,EType *e)
{
    int j=0;
    LinkList p = head;
    LinkList s;
    while(p && j<i-1)
    {
        j++;
        p = p->next;
    }
    if(!p->next || j>i-1)
        return ERROR;
    s = p->next;
    p->next = s->next;
    *e = s->data;
    free(s);
    return OK;
}

//头插法:在头结点head之后插入数据
void ReverseList1(LinkList head)
{
    LinkList p = head->next;
    LinkList q;
    while(p->next)//第一轮将:第二个结点拉到头结点与第一个结点之间
    {
        q = p->next;
        p->next = q->next;
        q->next = head->next;
        head->next = q;
    }
}

//递归实现逆序打印(不改变链表结构)
void ReversePrint(LinkList head)
{
    if(head->next)
        ReversePrint(head->next);
    printf("%d ",head->data);
}

void Visit(EType e)
{
    printf("%d ",e);
}

void TraverseList(LinkList head,void (*visit)(EType))
{
    LinkList p = head->next;
    while(p)
    {
        visit(p->data);
        p = p->next;
    }
}

int main()
{
    LinkList L;
    InitList(&L);

    for(int i=1;i<10;i++)
        InsertElem(L,i,i);

    TraverseList(L,Visit);
    printf("\n*****1******\n");

    ReversePrint(L->next);
    printf("\n*****2******\n");

    ReverseList1(L);
    TraverseList(L,Visit);
    printf("\n*****3******\n");

    int nLen = GetLength(L);
    printf("\n*****%d******\n",nLen);

    int e;
    GetElem(L,1,&e);
    printf("\n*****%d******\n",e);

    int pos;
    FindElem(L,8,&pos);
    printf("\n*****%d******\n",pos);

    ClearList(L);
    nLen = GetLength(L);
    printf("\n*****%d******\n",nLen);

    DestroyList(&L);
    printf("\n*****OV******\n",nLen);

    return 0;
}
  • 代码总结

    • 在创建链表和销毁链表的函数中,函数的入参应是链表的头结点(指针)地址&L

    • 在进行链表的其他操作时,函数入参不必是头结点(指针)地址,直接传入头结点即可,因为结点之间的关系使用指针表示的

    • 遍历的时候从第一个结点(非头结点)开始较好些

    • 逆序头插法示意图

这里写图片描述

链表应用

  • 实现一元多项式的加法
//数据结构
typedef struct Node
{
  float coef;//系数
  int expn;//指数
  struct Node *next;
}*LinkList;
//思路:将两个链表相同指数的系数相加

猜你喜欢

转载自blog.csdn.net/robothj/article/details/80467811