3.1 单链表

1.单链表简介

   数组在编程语言中用的非常有用,但是属数组至少有两个缺点

 (1) 编译时就得知道数组的大小

 (2) 在计算机内存中是连续存储的,这就意味着在数组中插入或删除一个数据,就需要调整其他数据。

而使用链表结构就不存在这些问题

下图表示一个链表的结构及其构造过程。


上图所示的链表中的每个节点都是下面类定义(节点值为整数型)的一个实例:

这是一种C++面向对象的写法,在C语言中,我们习惯定义成结构体。

 class IntSLLNode { 
     public:   IntSLLNode() {//第一个构造函数初始化next指针为NULL	
     next = 0;	 }   
     IntSLLNode(int i, IntSLLNode *in = 0) {//第二个构造函数设置两个成员数据的值	
     info = i; next = in;}
     int info;
     IntSLLNode *next;//指向下一个节点,这个节点的类型与本节点相同,这里要定义成公有成员,以便实施链表的操作 };


节点包含两个数椐成员: info 和 next,  info 成员用于对用户有用的存储, next 是用于连接链表节点对从而形成链表结构。

有了这个定义我们就可以构造上图所示的链表了:

IntSLLNode *p = new IntSLLNode(10);

p->next = new IntSLLNode(8);

p->next->next = new IntSLLNode(50);

这里仅定义了一个头指针p来访问数据,不是很方便,在实际操作中一般会定义两个指针或更多指针方便操作。


2.单链表操作

链表节点类型已经由类IntSLLNode定义好了,接下来就是定义一个类实施对链表的各种错作,单链表的操作无非是增删查改

定义下面的类IntSLList

  1. class IntSLList {
    public:
       IntSLList() {
           head = tail = 0;
       }
       ~IntSLList();
       int isEmpty() {
           return head == 0;
       }
       void addToHead(int);
       void addToTail(int);
       int  deleteFromHead(); // delete the head and return its info;
       int  deleteFromTail(); // delete the tail and return its info;
       void deleteNode(int);
       bool isInList(int) const;
       void printAll() const;
    private:
       IntSLLNode *head, *tail;//两个私有成员变量,分别是指向单链表的头和尾的指针
    };

2.1 插入

(1)添加到链表开头

四步:

  • 创建一个空节点

  • 初始化节点值el

  • 添加新节点到链表最前面,并将新节点的next指向头节点,也就是head的当前值

  • 将head更新为指向新节点的指针

void IntSLList::addToHead(int el) {    
      head = new IntSLLNode(el,head);//一句话就完成了四步,这就是C++的魅力了    
      if (tail == 0)       
          tail = head;
}

(2)添加到链表末尾

四步:

  • 创建一个空节点

  • 初始化节点值el,因为该节点要添加到链表的末尾,所以将新节点的next设置为NULL

  • 让原链表的尾节点的next指向新建的尾节点,这样就将新节点添加到了末尾

  • 将tail更新为指向新节点的指针

void IntSLList::addToTail(int el) {  
  if (tail != 0) {      // 先判断链表是否为空         
     tail->next = new IntSLLNode(el);         
     tail = tail->next;    
  }    
  else 
     head = tail = new IntSLLNode(el);}


2.2 删除

(1)删除头节点并返回它的值

为了程序的严谨需要考虑两种特殊情况:

  • 一种是试图从一个空链表删除一个节点。

  • 二是链表只有一个节点的情况。

int IntSLList::deleteFromHead() {
    if(!isEmpty())//检查链表链表是否为空
    {    
    int el = head->info;     
    IntSLLNode *tmp = head;     
    if (head == tail)     //加入链表只有一个节点;        
       head = tail = 0;     
    else 
      head = head->next;     
     delete tmp;     
     return el;    
     }   
    else   //std::cout<<"The List is Empty!"       return 0;}

注意:上面的程序实际上有一个问题,程序开始用if语句检查了链表是否为空,假如为空就就会执行else的操作返回0;而对于调用

这个方法的语句并不知道返回的0是头节点本身的值是0,还是因为链表为空而返回的0。最好的解决方法是调用此方法之前就判定一

下链表是否为空。

(2)删除尾节点并返回它的值

该删除操作由成员函数 deleteFromTail实现。 问题在删除了尾节点之后, tail应当指向链表的新末尾节点,也就是说, tail 必须反方向移动一个节点,但是因为从最后一个节点到它的前驱节点没有直接的链接,所以无法进行反方向移动。 因此必须从链表的幵头查找这个前驱 节点,并恰好在tail前面停止。这个任务是通过在for在 循环中用个临时变量temp遍历链表完成的 。

int IntSLList::deleteFromTail() {   
 int el = tail->info;   
  if (head == tail) {   // if only one node on the list;         
   delete head;        
   head = tail = 0;    
   }   
  else {                // if more than one node in the list,        
    IntSLLNode *tmp; // find the predecessor of tail;        
     for (tmp = head; tmp->next != tail; tmp = tmp->next);         
     delete tail;         
     tail = tmp;      // the predecessor of tail becomes tail;         t
     ail->next = 0;   
    }   
      return el;
 }

(3) 删除值为el的节点

前面讨论的这两种刪除操作是从开头或者末尾删除一个节点。如果要刪除一个包含特定整数的节点,而不关心这个节点在链表中的位置就需要使用不同的方法。这个节点也许正好在链表的开头、末尾,或者在链表中的任何位置。简单地说,必须先找到这个节点, 然后将其前驱节点与后继节点连接,从而将其从链表中删除。因为不知道该节点在什么位置, 操作复杂得多。

将前驱结点以及后继节点连接起来就 能从链表中删除这个节点 ,但是因为链表只有向后的链接,因此无法从某个节点获得其前驱。完成这个忏务的方法之一是先扫描链表找到处要删除的节点,然后再次扫描链表找到它的前驱,另一种方法如下图所示,借助两个指针变量pred和tmp。

前面的图只讨论了 一种情况,还有下面几种情况:

  • 试图从空链表中刪除节点,此时函数立即退出。

  • 从单节点链表中删除唯一的节点: head 和 tail都设置成 null

  • 从至少有两个节点的链表中刪除第一个节点,此吋需要更新 head

  •  从至少有两个节点的链表中刪除最后一个节点,此吋需要更新 tail

  •  链表中不存在包含某个数字的节点:不进行任何操作.

void IntSLList::deleteNode(int el) {
    if (head != 0)                     // if non-empty list;
    if (head == tail && el == head->info) { // if only one
        delete head;                       // node on the list;
        head = tail = 0;
    }
    else if (el == head->info) {  // if more than one node on the list
        IntSLLNode *tmp = head;
        head = head->next;
        delete tmp;              // and old head is deleted;
    }
    else {                        // if more than one node in the list
        IntSLLNode *pred, *tmp;
        for (pred = head, tmp = head->next; // and a non-head node
            tmp != 0 && !(tmp->info == el);// is deleted;
            pred = pred->next, tmp = tmp->next);
        if (tmp != 0) {
            pred->next = tmp->next;
            if (tmp == tail)
                tail = pred;
            delete tmp;
        }
    }
}

2.3 查找

插入和删除操作都对链表进行了修改。查找操作扫描已有的链表,以确定其中是否包含某个数, 在此用布尔成员函数 isInList()实现这个操作。

bool IntSLList::isInList(int el) const {    
  IntSLLNode *tmp;    
  for (tmp = head; tmp != 0 && !(tmp->info == el); tmp = tmp->next);    
  return tmp != 0;
}

完整代码:http://www.oschina.net/code/snippet_588162_48571


来自为知笔记(Wiz)


转载于:https://my.oschina.net/star91/blog/424879

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值