数据结构面试之二——双向链表表、循环链表、有序链表的常见操作

简介: 双向链表的建立是在单链表的基础上,多了一个指向前驱的指针back。其他的操作类似,注意点就是在双向链表的操作,尤其插入、删除操作中需要修改两个指针的指向,一个是back指针,一个是next指针。

数据结构面试之二——双向链表表、循环链表、有序链表的常见操作

题注:《面试宝典》有相关习题,但思路相对不清晰,排版有错误,作者对此参考相关书籍和自己观点进行了重写,供大家参考。


二、双向链表


双向链表的建立是在单链表的基础上,多了一个指向前驱的指针back。其他的操作类似,注意点就是在双向链表的操作,尤其插入、删除操作中需要修改两个指针的指向,一个是back指针,一个是next指针。


1.双向链表的构建【前面插入】


构建双向链表注意点:1)修改first指针(头指针)的指向。2)修改back、next指针。


//反向表头插入,从前面插入...



template<typename Type>

nodeType<Type>*doublyLinkedList<Type>::buildListBackward()

{

      nodeType<Type> *newNode;

      int num;

      cout << " Enter a list of integer end with -999." << endl;

      cin >> num;

      while(num != -999)

      {

             //..add

             newNode = new nodeType<Type>;

             newNode->info = num;

             newNode->next = NULL;

             newNode->back = NULL;

         

             if(first == NULL)

             {

                    first = newNode;

             }

             else

             {

                    newNode->next = first;

                    first->back = newNode;

                    first = newNode;

             }

             cin >> num;

      }

      return first;

}

2.双向链表的插入节点【此处考虑插入后保证有序,直接插入排序】


节点的插入同单链表,依然需要考虑多种因素。分以下几类:


1)双向链表为空,“提示信息”并返回。


2)双向链表非空,待插入节点的元素值小于first节点,需要修改first指针。


3)双向链表非空,待插入节点的元素值为链表中间的节点,需修改back、next指向。


4)双向链表非空,待插入节点在最末尾节点以后,修改最末节点的指针。



//双向不循环链表

template<typename Type>

voiddoublyLinkedList<Type>::insertNode(const Type& insertItem)  //new

{

nodeType<Type> *current;

nodeType<Type> *trailCurrent;

nodeType<Type> *newNode;

bool found = false;

newNode = new nodeType<Type>;

newNode->info = insertItem;

newNode->next = NULL;

newNode->back = NULL;

//case 1: 空表

if(first == NULL)

{

       first = newNode;

}

else

{

       current = first;

       while( current != NULL)

       {

              if( current->info >= insertItem )

              {

                     found = true;

                     break;

              }

              else

              {

                     trailCurrent = current;

                     current = current->next;

              }

       }

       //case 2: 第一个节点

       if(current == first)

       {

              current->next = newNode;

              newNode->back = current;

       }

       else

       {

              //case 3: 中间节点

              if(current != NULL)

              {

                     newNode->next = trailCurrent->next;

                     current->back = newNode;

                     trailCurrent->next = newNode;

                     newNode->back = trailCurrent;

              }

              else  //case 4:最后一个节点前

              {

                     trailCurrent->next = newNode;

                     newNode->back = trailCurrent;

              }//end case 4

       }

}

}

3.双向链表的删除节点


同样需要考虑以下几点:


1) 链表为空,“提示信息”并返回。


2) 链表非空,查找删除的元素在链表是否存在。是的话,found=true;否的话,found=false。


3) 如果没有找到包含查找元素的节点,“错误提示”并返回。


4) 如果找到,主要节点位置。如果是头节点或末尾节点主要修改first指针,及节点的back、next指向;如果不是头节点或者末尾节点就是中间节点,主要back、next的指向,完成插入。



template<typename Type>

void doublyLinkedList<Type>::deleteNode(constType& deleteItem)   //new

{

 nodeType<Type> *current = new nodeType<Type>;

 nodeType<Type> *trailCurrent;

 bool found = false;

     

 //case 1: 空表

 if(first == NULL)

 {

        cout << "The List is NULL!" << endl;

        exit(1);

 }

 else

 {

        current = first;

        while( current != NULL)

        {

               if( current->info == deleteItem )

               {

                      found = true;

                      break;

               }

               else

               {

                      trailCurrent = current;

                      current = current->next;

               }

        }

        if(found)

        {

               //case 2: 第一个节点

               if(current == first)

               {

                      first = current->next;

                      delete current;

               }

               else

               {

                      //case 3: 中间节点

                      if(current != NULL)

                      {

                             if(current->next != NULL)  //case3.1:要删除的是中间节点.

                             {

                                    trailCurrent->next =current->next;

                                    current->next->back = trailCurrent;

                                    delete current;

                             }

                             else

                             {

                                    trailCurrent->next = NULL;//case3.2:要删除的为最后一个节点.

                                    delete current;

                             }

                      }

           

               }// end else

        }// end if

        else

        {

               cout << "The elem " <<deleteItem << " is not Exist in the List!" << endl;//case 4: 链表中无此节点.

        }

 }//end else

}//end

三、循环链表


      循环链表能保障从每一个节点出发都能检索链表。即:last指针的指向不再为空,而是指向了first(头结点)。


      循环链表在构建链表、查找元素、插入、删除、操作中要注意修改末尾节点指针的指向,保证链表的循环。


1.      前插式构建循环链表


【思路】:每次在first指针前面插入节点;插入每个节点后注意修改last->link的指向。



template<typename Type>

void cycleList<Type>::bulidCycListBackward() //前插构造循环链表.

{

       Type newItem;

       while(cin >> newItem, newItem != -999)

       {

              nodeType<Type>* newNode = newnodeType<Type>;

              newNode->info = newItem;

              newNode->link = NULL;

              if(first == NULL)

              {

                     first = newNode;

                     last = newNode;

                     first->link = first;

                     last->link = first;

              }

              else

              {

                     newNode->link = first;

                     last->link = newNode;

                     first = newNode;

              }

       }

       cout << "输入完毕!" << endl;

}

2.      循环链表插入元素[后插入]


【思路】:同构造循环链表,每次在last指针后面插入节点;插入每个节点后注意修改last->link的指向。



//只在last末尾插入

template<typename Type>

voidcycleList<Type>::insertCycList(const Type& newItem)

{

nodeType<Type> *newNode = new nodeType<Type>;

newNode->info = newItem;

newNode->link = NULL;

if(first == NULL)        //链表为空...

{

       first = newNode;

       last = newNode;

       first->link = first;

       last->link = first;

}

else                   //链表非空...

{

       last->link = newNode;

       newNode->link = first;

       last = newNode;

}

cout << newItem << "was inserted!" <<endl;

}

3.      删除循环链表节点


考虑到是否为空链表、删除元素在链表中不存在、及节点存在(节点的位置可能为共分头、中间、尾),所以分为以下5种情况分别处理。:



//case1:链表为空。

//case2:链表非空,删除节点为头节点。

//case3:链表非空,删除节点为尾节点。

//case4:链表非空,删除节点为中间节点。

//case5:链表非空,不存在=deleteItem的节点。

template<typename Type>

voidcycleList<Type>::delCycList(const Type& deleteItem)

{

nodeType<Type> *current = new nodeType<Type>;

nodeType<Type> *trailCurrent = new nodeType<Type>;

nodeType<Type> *temp ;

bool found = false;

if( first == NULL )

{

       cout << "The List is Empty!" << endl;//case1

       return;

}

if( first->info == deleteItem )      //case2

{

       temp = new nodeType<Type>;

       temp = first;

       first = first->link;

       last->link = first;

       cout << "The node has" << deleteItem<< " was deleted! " << endl;

       delete temp;

       return;

}

else                                             //cas3,case4.需要查找后定位。

{

       current = first->link;

       while( !found && current != first)

       {

              if( current->info == deleteItem )

              {

                     found = true;

                     break;

              }

              else

              {

                     trailCurrent = current;

                     current = current->link;

              }

       }// end while

       if(found)

       {    

              temp = new nodeType<Type>;

              if(current == last)                        //case3

              {

                     temp = last;

                     trailCurrent->link = last->link;       //last->link = first

                     last = trailCurrent;

              }

              else

              {

                     temp = current;

                    trailCurrent->link= current->link;

              }

              cout << "The Elem : " <<deleteItem << " was deleted! " << endl;

              delete temp;

       }

       else

       {

              cout << "The Elem : " <<deleteItem << " was not Exist in the List! " << endl;

       }

}//end else

}

四、有序链表


注意:此处有序链表无非是在之前的一、单链表的操作的基础上,在插入元素的时候,按顺序插入,考虑的排序。


为了体现有序的特点,特通过递归实现了有序链表的逆序打印。关于有序链表或者链表的非递归实现,可以通过栈实现。下一节会分析并实现。


1.      有序链表的插入


考虑以下三种情况:


1) 当前链表为空;


2) 当前链表非空,要插入的元素值小于头结点的元素值;


3) 当前链表非空,要插入的元素值大于头结点的元素值,可以考虑找到第一个大于其的元素则停止搜索,插入其前即可。



template<typename Type>

void orderedLinkedListType<Type>::insertNode(constType& newItem)

{

nodeType<Type>* current;

nodeType<Type>* trailCurrent;

nodeType<Type>* newNode;

bool found = false;

newNode = new nodeType<Type>;

newNode->info = newItem;

newNode->link = NULL;

if(first == NULL)                           //case1:链表为空.

{

       first = newNode;

       last = first;

}

else

{

       current = first;                    

       while( !found && current != NULL)       //循环查找

       {

              if(current->info >= newItem)

              {

                     found = true;

                     break;

              }

             else

              {

                     trailCurrent = current;

                     current = current->link;

              }

       }//end while

       //first 特殊处理..

       if(current == first)                //case2:新插入的元素小于first节点的元素值..

       {

              newNode->link = first;        

              first = newNode;

       }

       else //其他..

       {

              newNode->link = current;       //case3:新插入的节点在非first位置

              trailCurrent->link = newNode;

       }

}//end else

}

2.      递归实现有序链表的逆序打印



template<typename Type>

voidorderedLinkedListType<Type>::printListReverse() const   //逆序打印.

{

    reversePrint(first);

    cout << endl;

}

//递归实现单链表的逆序打印.

template<typename Type>

voidorderedLinkedListType<Type>::reversePrint(nodeType<Type>* current)const  //逆序打印

{

if(current != NULL)

{

       reversePrint(current->link);

       cout << current->info << "\t";

}

}


相关文章
|
8小时前
|
存储
【数据结构】双向带头循环链表的实现
【数据结构】双向带头循环链表的实现
|
7小时前
【数据结构】单链表之--无头单向非循环链表
【数据结构】单链表之--无头单向非循环链表
|
7小时前
|
存储 算法 C语言
线性表,双向链表,静态链表,循环链表(约瑟夫环)(上)
线性表,双向链表,静态链表,循环链表(约瑟夫环)
42 5
|
7小时前
|
算法 搜索推荐 大数据
数据结构面试常见问题
V哥在工作中整理了22个常用数据结构实现与原理分析,在面试中可以帮你你充分准备
|
7小时前
|
Cloud Native 关系型数据库 MySQL
云原生数据仓库产品使用合集之在ADB中,如何将源数据的多表(数据结构一致)汇总到一张表
阿里云AnalyticDB提供了全面的数据导入、查询分析、数据管理、运维监控等功能,并通过扩展功能支持与AI平台集成、跨地域复制与联邦查询等高级应用场景,为企业构建实时、高效、可扩展的数据仓库解决方案。以下是对AnalyticDB产品使用合集的概述,包括数据导入、查询分析、数据管理、运维监控、扩展功能等方面。
|
7小时前
|
算法 搜索推荐 Python
数据结构与算法在Python面试中的应用实例
【4月更文挑战第13天】本文聚焦Python面试中的数据结构与算法问题,包括排序算法、链表操作和树图遍历。重点讨论了快速排序、链表反转和二叉树前序遍历的实现,并指出理解算法原理、处理边界条件及递归操作是避免错误的关键。通过实例代码和技巧分享,帮助面试者提升面试表现。
13 0
|
7小时前
|
存储 算法 C语言
上机实验二 设计单循环链表 西安石油大学数据结构
上机实验二 设计单循环链表 西安石油大学数据结构
34 1
|
7小时前
【数据结构】3道经典面试题带你玩转栈与队列
【数据结构】3道经典面试题带你玩转栈与队列
29 0
|
8小时前
|
算法 索引
【数据结构】10道经典面试题目带你玩转链表
【数据结构】10道经典面试题目带你玩转链表
93 0
|
7小时前
|
存储 算法
【数据结构与算法】【腾讯阿里链表面试题】算法题--链表易懂版讲解
【数据结构与算法】【腾讯阿里链表面试题】算法题--链表易懂版讲解