双链表的基本操作

   在单链表的基本操作中简单说明了单链表相关的问题,双链表与单链表极为相似,所不同的是,双链表的每个结点除了包含数据域data,指向后继结点的指针域next外,多了一个指向前一结点的指针域pre。这样一来,从某一个结点开始,不仅可以向后遍历链表,还可以向前遍历链表。

   对应单链表的基本操作来看,双链表的基本操作同样包括创建链表、插入结点、删除结点、获取链表长度、逆置链表、链表排序、清空链表、销毁链表。对应操作的方式也基本相似,但是还是有些需要特别注意的地方。

   双链表中每个结点都多了一个指针,对于某些操作来说,更加方便了,比如删除结点时,只需要一个临时指针即可。但是,因为要操作更多的指针,为了防止误操作,就要考虑更多的边界条件了。

  • 插入结点时,基本的操作过程是,根据给定的数据创建一个新结点newNode,然后将newNode插入到head和首结点之间,在此过程中需要对首结点的指针域赋值,但是,如果链表本身就是一个空链表,那么首结点是不存在,此时任何通过首结点指针操作数据的行为都将导致运行时错误。所以,插入第一个结点的时候,要特殊处理。

  • 删除结点时,基本的操作过程是,定位好待删除结点temp后,将temp结点从链表中抽走,只要让temp结点的前序结点next指针指向temp结点的后续结点,同时让temp结点的后续结点的pre指针指向temp结点的前序结点即可。因为定义了头结点,所以无论待删除的结点位于链表中何处,其前序结点都是存在的,但是,如果待删除的结点时链表中的最后一个结点,那么其后续结点也是不存在的,此时只需要将其前序结点的next指针置空即可。所以,删除最后一个结点时,要特殊处理。

  • 清空链表的本质就是逐个删除结点,因此,也涉及到删除最后一个结点,需要特殊处理。

  • 相比于单链表的逆置,双链表的逆置要直观一点,只需借助两个指针变量ptemp(因为可以通过pre指针访问前序结点)。

  • 双链表的排序仍然采用冒泡排序,主要目的还是尽量避免指针的操作,减小出错的可能性。对于冒泡排序,要注意每次循环比较的次数。


代码实现

DoubleLinkedList.h

#ifndef _DOUBLE_LIST_H_
#define _DOUBLE_LIST_H_

#include <stdio.h>
#include <stdlib.h>
#include <iostream>
#include <iomanip>
using namespace std;

typedef struct node {
    int data;
    struct node * pre;
    struct node * next;
}dNode;

dNode * createDList();
int getLength(dNode * head);
void printDList(dNode * head);
bool insertNode(dNode * head, int e);
bool deleteNode(dNode * head, int idx);
void reverseDList(dNode * head);
void sortDList(dNode * head);
void emptyDList(dNode * head);
void destroyDList(dNode * head);

#endif

DoubleLinkedList.cpp

#include "DoubleLinkedList.h"

dNode * createDList()
{
    dNode * head;
    head = (dNode *)malloc(sizeof(dNode));
    if(!head){
        return NULL;
    }
    head->data = 0;
    head->pre = NULL;
    head->next = NULL;
    return head;
}

int getLength(dNode * head)
{
    if(!head){
        return -1;
    }
    return head->data;
}

void printDList(dNode * head)
{
    dNode * temp;
    if(!head){
        return;
    }
    temp = head->next;
    while(temp){
        cout << left << setw(5) << temp->data;
        temp = temp->next;
    }
    cout << endl;
}

bool insertNode(dNode *head, int e)
{
    dNode * newNode;
    if(head == NULL){
        return false;
    }
    newNode = (dNode *)malloc(sizeof(dNode));
    if(!newNode){
        return false;
    }
    newNode->data = e;
    newNode->next = head->next;
    newNode->pre = head;
    if(head->next){
        head->next->pre = newNode;
    }
    head->next = newNode;
    head->data ++;
    return true;
}

bool deleteNode(dNode * head, int idx)
{
    dNode * temp;
    int length,i;
    if(!head){
        return false;
    }
    length = getLength(head);
    if(idx>=length){
        return false;
    }

    temp = head->next;
    for(i=0;i<length;i++){
        if(i == idx){
            temp->pre->next = temp->next;
            if(temp->next){
                temp->next->pre = temp->pre;
            }
            temp->next = NULL;
            temp->pre = NULL;
            free(temp);
            head->data --;
            break;
        } else {
            temp = temp->next;
        }
    }
    return true;
}

void reverseDList(dNode * head)
{
    dNode * p, * temp;

    if(!head){
        return;
    }
    if(getLength(head) <= 1){
        return ;
    }
    p = head->next;
    temp = p->next;
    p->pre = NULL;

    while(temp){
        p->next = p->pre;
        p->pre = temp;
        p = temp;
        temp = p->next;
    }

    p->next = p->pre;
    p->pre = head;
    head->next = p;
}

void sortDList(dNode * head)
{
    int e;
    int length;
    int i,j;
    dNode * p, * q;

    if(!head){
        return ;
    }
    length = getLength(head);
    if(length <= 1){
        return;
    }

    for(i=1;i<length;i++){
        p = head->next;
        q = p->next;
        for(j=length-i;j>=1;j--){
            if(p->data > q->data){
                e = p->data;
                p->data = q->data;
                q->data = e;
            }
            p = q;
            q = p->next;
        }
    }
}

void emptyDList(dNode *head)
{
    dNode * temp;
    if(!head){
        return;
    }
    if(getLength(head) == 0){
        return;
    }
    temp = head->next;
    while(temp){
        temp->pre->next = temp->next;
        if(temp->next){
            temp->next->pre = temp->pre;
        }
        temp->next = NULL;
        temp->pre = NULL;
        free(temp);
        temp = head->next;
    }
    head->data = 0;
}

void destroyDList(dNode * head)
{
    if(!head){
        return;
    }
    emptyDList(head);
    free(head);
}

test.cpp

#include "DoubleLinkedList.h"

int main()
{
    int i;
    dNode * head;
    int rawData[] = {1,3, 2, 5, 7, 9, 6, 0};

    head = createDList();
    if(!head){
        cout << "can't create double linked list!" << endl;
    }

    for(i=0;i<sizeof(rawData)/sizeof(int);i++){
        insertNode(head,rawData[i]);
    }
    printDList(head);

    insertNode(head,10);
    cout << "current list length : " << getLength(head) << endl;
    printDList(head);

    deleteNode(head,8);
    printDList(head);

    reverseDList(head);
    printDList(head);

    sortDList(head);
    printDList(head);

    emptyDList(head);
    cout << "current list length : " << head->data << endl;

    destroyDList(head);

    return 0;
}

运行结果
运行结果

   指针真的是一把双刃剑,用得好,它能帮你轻而易举地完成自己想做的事,否则的话,一旦错误出现,你可能得绞尽脑汁才能发现问题所在。根据经验判断,一旦程序编译通过,运行着运行着就意外终止了,极有可能就是哪一处指针的操作出了问题。所以,当你看到一个指针的时候,一定要注意提醒自己,这是一个指针

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值