您的位置:首页 > 其它

双链表的基本操作

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

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

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

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

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

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

相比于单链表的逆置,双链表的逆置要直观一点,只需借助两个指针变量
p
temp
(因为可以通过
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;
}


运行结果



   指针真的是一把双刃剑,用得好,它能帮你轻而易举地完成自己想做的事,否则的话,一旦错误出现,你可能得绞尽脑汁才能发现问题所在。根据经验判断,一旦程序编译通过,运行着运行着就意外终止了,极有可能就是哪一处指针的操作出了问题。所以,当你看到一个指针的时候,一定要注意提醒自己,这是一个指针
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: