线性表之双链表

双链表实现

双链表是在单链表的基础上增加一个指向前一个节点的指针。使节点持有指向它前后两个节点的指针。从而实现逆序遍历。
双链表的数据操作与单链表相差不大,只比单链表多修改一个指针,在插入和删除操作时要对首节点和最后一个节点特殊处理。具体实现见代码

结构体分析

先来看一下结构体的定义,如下,person的结构里只有数据域和两个前后节点,是最精简的了,没有多余数据。

    typedef struct person2{
        char name[10];
        int age;
        struct person2 *next;
        struct person2 *last;

}person2;

再来看linkedList结构体的定义。如下,这个结构体可以理解为是专门记录链表信息的管理型结构体,或者是专门操作person结构的工具。所以他持有链表头尾节点,表长。每条链表只含有有一个linkedList结构,或者说每一个linkedList代表一条链表。
这样linkedList里的数据就只有一份,空间复杂度O(1),已是最低了。
这里称linkedList里封装的head节点为头结点head,它的next指向链表第一个元素,称为首节点。所以头结点是不包含在链表上的,只是head节点的next指针指向首节点,end节点的last指针指向链表最后一个节点。

typedef struct linkedList{
    struct person2 *head;
    struct person2 *end;
    struct person2 *before;//先序遍历用的指针
    struct person2 *after;//后序遍历用的指针

    int length;
}linkedList;
//2018/1/25 修改: 这里的head是指链表的第一个元素, 用frist更合适一点的。

实现代码如下:

//新建person
person2 *newP2(char *name,int age){


    //动态分配内存,初始化person
    person2 *p = (person2 *)malloc(sizeof(person2));
    p->age = age;
    strcpy(p->name,name);
    p->next = NULL;
    p->last = NULL;
    return p;
}

//建立空表
linkedList *newList(){

    //建立头结点
    linkedList *link = (linkedList *)malloc(sizeof(linkedList));

/*
这里之所以给头尾指针分配实体(内存), 是因为对于双链表来说, 空表添加第一个元素要特殊处理(第一个元素要让首尾节点都指向它),
那么这里给首尾节点分配实体,就相当于新链表初始化就有两个节点, 就不存在空表的情况了, 也就不需要处理前述问题了.
java里面的LinkedList的实现方式是每次添加元素都判断是否是空表,如下:

void linkLast(E e) {
  final Node<E> l = last;
  final Node<E> newNode = new Node<>(l, e, null);
  last = newNode;
  if (l == null)
    first = newNode;
  else
    l.next = newNode;
  size++;
  modCount++;
}



*/ //为头尾指针分配实体 link->head = newP2("",-1); link->end = newP2("",-1); //使头尾互指 head->last和end->next 为null, link->head->next = link->end; link->end->last = link->head; link->after = link->end; link->before = link->head; link->length = 0; return link; } //add element in first void addFirst(linkedList *link, person2 *p){
//这里的head是链表第一个节点, 首结点 person2 *head = link->head->next; //让新节点的next指向head->next p->next = head; //让head->next->last指向新元素 head->last = p; //修改队首指针 link->head->next = p; link->length++; } //add element in end void addEnd(linkedList *link, person2 *p){ person2 *end = link->end->last; //让新节点p的last指针指向最后一个元素end 让新元素p的last指向end->last; p->last = end; //让end 所指节点的next指针指向新节点p end->next = p; //修改队尾指针 link->end->last = p; link->length++; } //根据index序号读表元素,返回数据域data(index序号按写入顺序,从0开始) person2 *read(linkedList *link, int index){ if(link->length > index && index >= 0 ){ person2 *head = link->head->next; int j = 0; while (head != NULL && j < index){ head = head->next; j++; } if(j == index){ return head; } } return NULL; } //定位元素 返回下标(从0开始) int locatep2(linkedList *link, person2 *p){ int i; person2 *head = link->head->next; for(i = 0; head != NULL; i++){ if(compareP2(head,p)){ return i; } head = head->next; } return -1; } //person 比较器 int compareP2(person2 *p1,person2 *p2){ if(p1 == p2){ return 1; } return (p1->age == p2->age && !strcmp(p1->name,p2->name)); } //insert element before index //实现思路:先找到index节点(调用read()),在修改指针。 void insertlink(linkedList *link,person2 *p,int index){ person2 *p1; //得到要插入位置的元素 if(index == 0 ){ addFirst(link,p); return; }else if(index == link->length){ addEnd(link,p); return; }else{ p1 = read(link,index); } //insert element if(p1 != NULL){ //新元素p的next指向index处元素p1 p->next = p1; //新元素p的last指向index处元素p1的last p->last = p1->last; //修改插入位置两边的节点指向,使其指向新节点p(元素 == 节点,原谅我懒得改成统一) p1->last = p; p->last->next = p; link->length++; }else{ printf("index not find"); exit(1); } } //delete element by index //实现思路:先找到index节点(调用read()),再修改指针。 void myRemove2(linkedList *link,int index){ person2 *p1; if(index == 0 ){ //指向第一个节点的head person2 *head = link->head; //待删除元素p1指向首节点 p1 = head->next; //使head指向下下一个节点 head->next = head->next->next; //使现在head的下一个元素的last指针为null(便于遍历元素) head->next->last = NULL; } else if(index == link->length-1){ person2 *end = link->end; p1 = end->last; end->last = end->last->last; end->last->next = NULL; } else{ //不是首尾节点的情况 //得到要插入位置的节点 p1 = read(link,index); //delete element if(p1 == NULL){ printf("index not find"); exit(1); } p1->last->next = p1->next; p1->next->last = p1->last; } free(p1);//一定要释放节点空间 link->length--; } //先序遍历 person2 *before(linkedList *link){ person2 *p = link->before->next; if(p != NULL){ link->before = p; return p; } link->before = link->head; return NULL; } //后序遍历 person2 *after(linkedList *link){ person2 *p = link->after->last; if(p != NULL){ link->after = p; return p; } link->after = link->end; return NULL; } //print element void linkPrint(linkedList *head){ int length = head->length; person2 *begin = head->head->next; printf("***********length = : %d ************\n",length); for(;begin != NULL; begin = begin->next){ printf("name: %s,age: %d\n",begin->name,begin->age); } } void doubleLinkMain(){ //linkedList *p1; person2 *p2; //初始化 link list linkedList *head = newList(); addEnd(head,newP2("HH",0)); addEnd(head,newP2("HH",1)); addEnd(head,newP2("HH",2)); addEnd(head,newP2("HH",3)); addEnd(head,newP2("HH",4)); addEnd(head,newP2("HH",5)); linkPrint(head); addFirst(head,newP2("ff",1)); addFirst(head,newP2("ff",2)); addFirst(head,newP2("ff",3)); addFirst(head,newP2("ff",4)); addFirst(head,newP2("ff",5)); linkPrint(head); person2 *p1 = read(head,2); if(p1 != NULL){ printf("read index 2 :%s++%d\n",p1->name,p1->age); } printf("found person(ff,2) index is %d\n",locatep2(head,newP2("ff",2))); //test insert element printf("insert element 0 ,4 \n"); insertlink(head,newP2("new",66),0); insertlink(head,newP2("new",66),4); linkPrint(head); //test delete element printf("delete element 0 ,6, 12\n"); myRemove2(head, 0); myRemove2(head, 6); myRemove2(head, 10); linkPrint(head); printf("\n\n"); while((p2 = before(head)) != NULL){ printf("先序遍历 :%s,%d\n",p2->name,p2->age); } while((p2 = after(head)) != NULL){ printf("后序遍历 :%s,%d\n",p2->name,p2->age); } }
posted @ 2017-10-29 09:15  zhangwang010  阅读(374)  评论(0)    收藏  举报