数据结构:一、线性表及例子操作

                                                        数据结构
一、线性表
            线性表其实很简单,就是一条线的关系,除了最前面和最后的结点,每一个结点前面都有一个结点,叫前驱,每一个结点后面也有一个结点,叫后继。不过世上的事情都是很灵活的,你也可以把最后一个结点和最前一个结点链接起来,那样就成一个环了,那就叫循环链表。

    其实我们想一下,在编程的时候,对于一组数据我们的存储方式其实也就那么两种,一种是数组(这叫顺序储存),另一种就是动态分配内存,然后用一个指针把动态分配的各个空间链接起来(这叫链式储存)。对于链表的实现,我们也是只有这两种存储方式。对于一个数据结构,我们不单止要定义它是怎么构成的,还应该定义一些作用于它的操作,至于编程细节实现就因情况而异了。

            以下是一个用数组来模拟链式储存的例子:

在这个例子中,大概的思想是这样的:因为要模拟链表,所以就肯定要知道下一个可以用的结点是哪一个啦,不像顺序储存直接知道,所以我们需要两个链表,一个是用来管理存放数据的链表,另一个就是用来管理所有还没用到的链表,所以需要两个头结点,这里我们就选用一头一尾这两个结点,头的是用来管理备用结点链表的,尾的是用来管理存放数据结点链表的。
typedef char Element;

typedef struct {
    Element e;
    int cur;//游标,相当于链表的next
} Node;

typedef struct {
    Node str[MAX];
    int length;
} List;

//一些操作函数
void printList(List *a)
{
    int i =a->str[MAX-1].cur; //第一个存在数据的结点
    for( ; i ; i = a->str[i].cur)
            printf("%c ",a->str[i].e);
}

Element enterElem(void)
{
    Element c;
    while ((c=getchar()) ==' ') ;//把空格去掉
    return c;
}

int staMalloc(List *a)
{//把备用链表的结点取出来,就像删除操作一样
    int pos = a->str[0].cur; //等于第一个没用到的结点,当没有可用结点的时候,就是cur等于0,不用特别处理
    a->str[0].cur = a->str[pos].cur; //指向原本的第二个备用结点
    return pos;
}

void initialList(List *a)
{//注意要倒数两个要都要指向0,因为考虑到是两个独立链表,当链表满的时候,继续增加元素会怎样 ,当然,如果是写成循环链表就不用
    int i;
    for ( i=0; i<MAX-2; ++i)
        a->str[i].cur = 1+i;
    a->length = a->str[i].cur = a->str[i+1].cur = 0; //0的就相当于链式储存里面的NULL
}
            
void creatList(List *a)
{//静态链表由两部分组成,一个链表是存放空闲结点都链表(用0),还有一个是用来存放数据的链表(用MAX-1),这里采用头结点插入
    Element e;
    int i,j=MAX-1;
    while ( (e=enterElem()) != '\n') {
        if(!(i = staMalloc(a))) {
            puts("no more space!!!");
            return ;
        }
        a->str[i].cur  = a->str[j].cur;//新结点指向第一个数据结点的位置
        a->str[j].cur = i; //在把头结点的cur指向插入的结点
        a->str[i].e = e;
        ++a->length;
    }

}
void staFree(List *a,int pos)
{ //收回结点,就是把pos插入备用链表中
    a->str[pos].cur = a->str[0].cur;
    a->str[0].cur = pos;
}

int findPos(List *a,int pos)
{ //找到链表的第pos个元素并返回它的位置
    if ( pos<0 || pos>a->length)
            return 0;
    int i = 0,j=MAX-1;
    for ( ; i<pos; ++i) //从0开始到pos-1,刚好就是第pos个,因为从头结点,所以i从0开始,表示现在是第i个,一直累加
            j = a->str[j].cur;
    return j;
}
    

void deletePos(List *a, int pos)
{
    int p = findPos(a,pos-1),t; //先找到第pos-1个元素的位置,找前驱,方便删除操作
    t = a->str[p].cur; //要删除的元素
    a->str[p].cur = a->str[t].cur; //pos的前驱指向pos的后继,跳过pos
    staFree(a,t); //释放pos
    --a->length;
}


int insertPos(List *a,int pos,Element e)
{
    int i = findPos(a,pos-1),j,k;//一样先找到哦啊pos-1,这样方便链表的链接
    
    j = staMalloc(a);
    if(i&&j) { //如果找不到pos-1元素或者没有备用结点,那么就不止行,直接return 0了
        a->str[j].cur = a->str[i].cur;
        a->str[i].cur = j;
        a->str[j].e = e;
        return 1;
    }
    return 0;
}
————————————————————————————————————————————————————————————————————————————
  下面再来一个纯种的链式储存的例子:
list.h

typedef int Elem;
typedef struct Node{
        Elem e;
        struct Node *next;
        } Node;


typedef struct List {
        Node *head;
        int length;
        } List;
        
void initialList(List *list);//初始化链表,建立头结点。
void creatList(List *list);//建立链表
void printList(List *list);//打印链表
void cleanList(List *list);//清空链表
int listLength(List *list);//返回链表的长度
int isEmpty(List *list);//判断链表是否为空
Node* posNode(List *list,int pos);//找出第pos个结点,并返回地址
Node* findElem(List *list,Elem e);//找出第一个element是e 的结点,并返回地址
int posToX(List *list,int pos,Elem e);//把第pos个结点的值改为X
void addAtFront(List *list,Elem e);//在链表头部插入
void addAtRear(List *list,Elem e);//在链表尾部插入
int insertPos(List *list,int pos,Elem e);//把e插入链表 ,并使其成为第pos个元素
Elem deletePos(List *list,int pos);//删除第pos个元素,并返回element的值
void deleteX(List *list,Elem e);//删除链表中所有element为e的结点
void swapPosPos(List *list,int pos1,int pos2);//交换结点
void destroyList(List *list);//销毁

list.c

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


int enterElem(Elem *e)
{ //输入<0就放回0,表示输入结束
    scanf("%d",e);
    if(*e<0)
           return 0;
    return 1;
}



void initialList(List *list)
{
     list->head=(Node *)malloc(sizeof(Node)); //建立头结点
     list->head->next=NULL;
     list->length = 0;
}
 
//建立链表,输入小于0代表链表输入终止
void creatList(List *list)
{
     Elem e;
     Node *p = list->head;
     while(enterElem(&e)) {
          //尾结点插入
                      p->next = (Node*)malloc(sizeof(Node));
                      p=p->next;
                      p->e = e;
                      ++list->length;
     }
     p->next = NULL;//处理尾结点的next指向
}

void printList(List *list)
{
     Node *p=list->head->next;
     while(p) {
             printf("%d ",p->e);
             p = p->next;
     }     
}

void cleanList(List *list)
{
     Node *p = list->head->next;//指向第一个数据结点
     Node *temp;
     
     while(p) {
    //把空间free掉
              temp=p->next;
              free(p);
              p = temp;
     }
     list->length = 0;
     list->head->next = NULL;
}


int listLength(List *list)
{
    return list->length;
}


int isEmpty(List *list)
{ //返回1就是空,返回0就是不空
    return list->length > 0 ? 0 : 1;
}


Node* posNode(List *list,int pos)
{
      int i=0;
      Node *p=list->head;
      
      if(pos<0 || pos > list->length)//pos允许为0,这样才能对第一个存放数据的结点进行一些操作,但pos是0的时候,将会放回头结点
               return NULL;
      while (++i <= pos) //找到第pos个
            p=p->next;
      return p;
}


Node* findElem(List *list,Elem e)
{
      Node *p = list->head->next;
      
      while(p)
               if ( p->e == e)
                  return p;
               else
                   p = p->next;
      return NULL;
}


int posToX(List *list,int pos,Elem e)
{ //返回1就是修改成功
    Node *p = posNode(list,pos);
    if (p) {
       p->e = e;
       return 1;
    }
    return 0;
}


void addAtFront(List *list,Elem e)
{
     Node *p = (Node*)malloc(sizeof(Node));
     
     p->e = e;
     p->next = list->head->next;
     list->head->next = p;
}


void addAtRear(List *list,Elem e)
{
     Node *p = list->head;
     
     while(p->next)
                   p = p->next;
     p->next = (Node*)malloc(sizeof(Node));
     p = p->next;
     p->e = e;
     p->next = NULL;
}


int insertPos(List *list,int pos,Elem e)
{
    Node *p = (Node*)malloc(sizeof(Node)),*temp;
    p->e = e;
    if(temp = posNode(list,pos-1)) {//找前驱
           //注意下面这两行的顺序不能掉转哦
      p->next = temp->next; //新结点指向第pos个
            temp->next = p; //成为第pos个
            return 1;
    }
    return 0;
}


Elem deletePos(List *list,int pos)
{
     Node *p,*t;
     Elem e;
     
     if(p=posNode(list,pos-1)) {//找前驱
          t=p->next; //t指向要删除的结点
          e = t->e; //e记住要删除的数据,到时返回
          p->next = t->next; //前驱绕过要删除的结点
          free(t);
          return e;
     }     
     return -1;
}

void deleteX(List *list,Elem e)
{
     Node *p = list->head,*tmp;
     
     while(p->next)  //一直循环,当遍历完或者找到e为止
                    if(p->next->e == e) {                        
                                  tmp = p->next;
                                  p->next = tmp->next;
                                  free(tmp);
                    }
                    else
                                  p = p->next;
}

void swapPosPos(List *list,int pos1,int pos2)
{
     Node *p1,*p2,*t1,*t2;
     //先找到这两个位置的前驱,这样方便操作,t1,t2是指向要交换的结点
    //交换结点就是两个结点的前驱next交换,两个结点的next交换
     if((p1=posNode(list,pos1-1)) && (p2=posNode(list,pos2-1))){
         t1 = p1->next; t2 = p2->next;
         //因为记录了t1,t2,所以两个前驱的next直接交换
    p1->next = t2;
         p2->next = t1;
         //当p1是临时指针,用来交换t1,t2的next指针
         p1 = t1->next;
         t1->next = t2->next;
         t2->next = p1;
     }
}


void destroyList(List *list)
{
     cleanList(list);
     free(list->head);

}

————————————————————————————————————————————————————————————————————————————

多项式相加
  既然写出了,那么就在用这个结构来做一个简单的多项式相加吧。要怎么做呢?首先就是应该来定义个结构体来表示一个项啦,就是说有系数和指数。那么就是每个结点包含的就是系数、指数,还有指向下一个结点的指针。在多项式相加的时候,是次数相同的项,系数相加,不相同的就插进去就可以了。也就是说,我要同时遍历两个多项式的链表,对它们的次数进行比较,如果是相同就系数相加,如果是乱序的话很难做,因为那个不相等,可是可能不知什么地方就有个结点啊,这样的话每次都要遍历另一条链表一次,做起来又麻烦又慢。最好还是有序的,这样就方便多啦。那么就是说在创建链表的时候就要把它创建成有序的,而又不应该这样要求用户输入成有序的。那么怎么做呢?想一下,用类似归纳法的思想想一下,先假定成有序的了,然后再读取一个结点进去,要插入,这样要怎么做呢?就一直遍历下去,直到遇到有一个结点的次数比新结点的次数小或者相等就可以啦。在那里停住的话插入不好搞,所以就是要找符合结点的前驱,这样插入就很简单啦。这个问题想清楚了,还有个问题就是两条表插来插去的具体要怎么做的问题。因为我们是合并它们俩,所以我们只需要把它们的next指针改啊改,然它们串起来就可以了,好就这么定了
#include<stdio.h>
#include<stdlib.h>
 
typedef struct {
    float coef;//系数
    int expn;//指数
} Date;
 
typedef struct Node {
    Date x;
    struct Node *next;
} Node;
 
typedef struct {
    Node *head; 
} List;
 
void insert(List *L,Node *n,Node *s);
void creatList(List *L);
void addList(List *L1,List *L2,List *L3);
void printList(List *L);
 
int main(void)
{
    List L1,L2,L3;
         /******************************************************
      用freopen这个函数很方便,不用测试的时候每次都自己输入
      输入格式是4,7 3,5...然后以一个字符结尾表示输入完了,因为
      输入的时候我是用scanf,那么出现字符的时候就会输入失败
         *******************************************************/
    freopen("test","r",stdin);
    creatList(&L1);
    printList(&L1);
    creatList(&L2);
    printList(&L2);
    addList(&L1,&L2,&L3);
    printList(&L3);
    return 0;
}
 

void insert(List *L,Node *p,Node *s)
{//p是要插入的结点,s是符合要求的结点的前驱,就下面两句,一插就行啦
    p->next = s->next;
    s->next = p;
}
 
void creatList(List *L)
{
    Node *p,*s;
    int coef,expn;
    
    s = L->head = (Node*)malloc(sizeof(Node));//创建头结点
    L->head->next = NULL;  
    
    while(scanf("%d,%d",&coef,&expn)) {
        while(s->next!=NULL && s->next->x.expn >= expn) {
            //这个就是找到符合要求结点前驱的循环
            s = s->next;
            if(s->x.expn==expn) {
                s->x.coef += coef;
                break;
            }    
        }
        //然后就是给它找个空间,把东西放进去,再插入    
        p = (Node*)malloc(sizeof(Node));
        p->x.coef = coef;
        p->x.expn = expn;
        insert(L,p,s);
    }
    getchar();//把代表输入结束的那个字符处理掉
    
}
 
void addList(List *L1,List *L2,List *L3)
{
    Node *p1,*p2,*p3,*tmp;
 
    p3 = L3->head  = L1->head;
    p1 = L1->head->next;
    p2 = L2->head->next;
 
    while(p1&&p2) {
                  //指数大的进去L3那条表
        if(p1->x.expn > p2->x.expn) {  
            p3->next = p1;
            p3 = p1;
            p1 = p1->next;
        } else if (p1->x.expn < p2->x.expn) {
            p3->next = p2;
            p3 = p2;
            p2 = p2->next;
        } else {
        //想等的话就系数加起来,当然,还要考虑如果系数相加等于0要怎么办
        //加了之后,p2的结点就没有用了,把它free掉吧
            p1->x.coef += p2->x.coef;
            tmp = p2;
            p2 = p2->next;
            free(tmp);
            if(p1->x.coef < 0.000001) {//因为是浮点数
            //相加之后系数为0,那么p1也没用了,更新p1,把它free掉
                tmp = p1;
                p1 = p1->next;
                free(tmp);
            }
 
        }
    }
    p3->next = p1?p1:p2;//把剩下还没插进去的链表都插进去
}
 
void printList(List *L)
{
    Node *p = L->head->next;
 
    while(p) {
        printf("%.2fx^%d+",p->x.coef,p->x.expn);
        p = p->next;
    }
    puts("\b ");//先退一下格,在打印个空格上去,把尾部的+处理掉
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值