您的位置:首页 > 理论基础 > 数据结构算法

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

2012-03-12 19:27 260 查看
                                                        数据结构
一、线性表

            线性表其实很简单,就是一条线的关系,除了最前面和最后的结点,每一个结点前面都有一个结点,叫前驱,每一个结点后面也有一个结点,叫后继。不过世上的事情都是很灵活的,你也可以把最后一个结点和最前一个结点链接起来,那样就成一个环了,那就叫循环链表。
    其实我们想一下,在编程的时候,对于一组数据我们的存储方式其实也就那么两种,一种是数组(这叫顺序储存),另一种就是动态分配内存,然后用一个指针把动态分配的各个空间链接起来(这叫链式储存)。对于链表的实现,我们也是只有这两种存储方式。对于一个数据结构,我们不单止要定义它是怎么构成的,还应该定义一些作用于它的操作,至于编程细节实现就因情况而异了。

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

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

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);
4000
//删除第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 ");//先退一下格,在打印个空格上去,把尾部的+处理掉

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息