不带头节点不带环的单向链表的一些操作(1)

链表有很多种,有单项、双向链表,带环、不带环链表,带头节点、不带头节点链表,而这些链表又可以两两或者三个三个组成一种链表,下面主要是说带不头节点不带环单向链表的一些操作。

链表初始化


链表初始化有三种方法
方法一:
   4 //1.链表初始化
   5 void LinkListInit(LinkNode** node)                                                                       
   6 {
   7     *node = NULL;
   8 }
   9 
方法二:
  10 void LinkListInit2(LinkNode* node)
  11 {
  12     node = NULL;
  13 }
  14 
方法三:
  15 LinkNode* LinkListInit3()
  16 {
  17     return NULL;                                                                                         
  18 }
  19 

尾部插入一个节点——尾插

首先要创建一个新的节点
  20 LinkNode* CreateNode(LinkNodeType value)
  21 {                                                                                                        
  22     LinkNode *new_node=(LinkNode*)malloc(sizeof(LinkNode));
  23     new_node->data=value;
  24     new_node->next=NULL;
  25     return new_node;
  26 }
然后要判断链表是否合法,和链表是否为空,若链表为空,则这个问题就直接转化为创建一个新节点的问题了,若链表不为空,则设一个指针cur指向链表的头节点,并建立一个循环,让cur指针一直指向cur的下一个节点,直至cur的下一个节点为NULL,则循环结束,此时cur指针一定指向链表的最后一个节点,这时创建一个新节点,并让cur的next指向新创建的节点,并且让新创建的节点的next重新指向NULL,详细见图
不带头节点不带环的单向链表的一些操作(1)_第1张图片
  27 //2.尾插
  28 void LinkListPushBack(LinkNode** phead, LinkNodeType value)
  29 {
  30     if(phead == NULL)
  31     {                                                                                                      
  32     //非法输入
  33     return ;
  34     }
  35     if(*phead == NULL)
  36     {
  37     //空链表
  38     *phead=CreateNode(value);
  39     return ;
  40     }
  41     //链表非空
  42     LinkNode *cur=*phead;
  43     while(cur->next!=NULL)
  44     {
  45     cur=cur->next;
  46     }
  47     LinkNode *new_node=CreateNode(value);
  48     cur->next=new_node;
  49     new_node->next=NULL;
  50     return ;
  51 }


尾部删除一个节点——尾删

首先 要判断链表是否合法、是否为空,若链表链表只有一个元素,则这个问题就转化为销毁链表节点的问题

  53 void DestroyNode(LinkNode *node)
  54 {                                                                                                          
  55     //free(*node->data);
  56     free(node);
  57     return ;
  58 }
  59 
若链表不只有一个元素,则设一个指针cur指向链表的头节点,设一个指针pre指向NULL,并建立一个循环,让pre指针一直指向指针cur,cur指向cur的下一个节点,直至cur的下一个节点为NULL,则循环结束,此时cur指针一定指向链表的最后一个节点,而pre指针指向链表的倒数第二个节点,这时让pre的next指向NULL,并销毁cur节点,详细见图
不带头节点不带环的单向链表的一些操作(1)_第2张图片
  60 //3.尾删
  61 void LinkListPopBack(LinkNode** phead)
  62 {
  63     if(phead == NULL)
  64     {
  65     //非法输入
  66     return ;
  67     }
  68     if(*phead==NULL)
  69     {
  70     //空链表
  71     return ;
  72     }
  73     if((*phead)->next==NULL)
  74     {
  75     //只有一个元素
  76     DestroyNode(*phead);
  77     *phead=NULL;
  78     return ;
  79     }
  80     LinkNode *cur=*phead;                                                                                  
  81     LinkNode *pre=NULL;
  82     while(cur->next!=NULL)
  83     {
  84     pre=cur;
  85     cur=cur->next;
  86     }
  87     //当循环结束,cur指向最后一个节点,pre指向倒数第二个节点
  88     pre->next=NULL;
  89     DestroyNode(cur);
  90     return ;
  91 }
  92 

头部插入一个节点——头插

首先 要判断链表是否合法,然后要创建一个新的节点new_node,让new_node的next指向链表的头节点,再让链表的头节点等于new_node,详细见图
不带头节点不带环的单向链表的一些操作(1)_第3张图片
  93 //4.头插
  94 void LinkListPushFront(LinkNode** phead,LinkNodeType value)
  95 {
  96     if(phead == NULL)
  97     {
  98     //非法输入
  99     return ;
 100     }
 101     LinkNode* new_node=CreateNode(value);
 102     new_node->next=*phead;
 103     *phead=new_node;
 104 }
 105                                                                                                            

头部删除一个节点——头删

首先 要判断链表是否合法、是否为空,然后定义一个指针to_erase指向要删除的元素—头节点phead,让原先的头节点phead,指向第二个节点,然后销毁to_erase几点 ,详细见图
不带头节点不带环的单向链表的一些操作(1)_第4张图片

 106 //5.头删
 107 void LinkListPopFront(LinkNode** phead)
 108 {
 109     if(phead == NULL)
 110     {
 111     //非法输入
 112     return ;
 113     }
 114     if(*phead == NULL)
 115     {
 116     //空链表
 117     return ;
 118     }
 119     LinkNode* to_erase=*phead;
 120     *phead=(*phead)->next;
 121     DestroyNode(to_erase);
 122     return ;
 123 }

将一个新节点插入到pos位置之后

首先要创建一个新的节点new_node,然后让new_node的next指向pos位置的next,再让pos位置的next指向new_node,详细见图
不带头节点不带环的单向链表的一些操作(1)_第5张图片
 125 //6.将一个新节点插入到pos之后
 126 void LinkListInsert(LinkNode* phead,LinkNode* pos,LinkNodeType value)
 127 {
 128     if(pos == NULL)
 129     {
 130     //非法输入
 131     //pos表示一个节点的指针,若pos为空,则表示根本不存在这样的节点
 132     return ;
 133     }
 134     LinkNode* new_node=CreateNode(value);
 135     new_node->next=pos->next;
 136     pos->next=new_node;
 137     return ;
 138 
 139 }


将一个新节点插入到pos位置之前

首先要判断pos位置在哪,若pos为第一个元素,则问题转化为链表的头插法;若pos位置不是链表的第一个元素,则要对链表进遍历来找到pos位置,若找到了就进行插入操作,若没找到,则不进行操作

 141 将一个新节点插入到pos之前
 142 void LinkListInsertBefore(LinkNode** phead,LinkNode* pos,LinkNodeType value)                               
 143 {
 144     if(phead == NULL || pos == NULL)
 145     {
 146     //非法输入
 147     return ;
 148     }
 149     if(*phead == pos)
 150     {
 151     //要插入的位置为头结点
 152     LinkListPushFront(phead,value);
 153     return ;
 154     }
 155     LinkNode *cur=*phead;
 156     for(;cur!=NULL;cur=cur->next)
 157     {
 158     if(cur->next==pos)
 159     {
 160         break;
 161     }
 162     }
 163     //循环结束后要知道是由于哪种情况导致的循环结束到底找没找到pos
 164     if(cur == NULL)
 165     {
 166     //没找到
 167     return ;
 168     }
 169     LinkListInsert(phead,cur,&value);
 170     return ;
 171 }
 172                                                                                                            
 173 
上面 这种方法的时间复杂度为O(n),下面将对上面的代码进行优化,使时间复杂度从O(n)变为O(1)
即先将元素插入到pos位置之后,再将Pos位置的元素和新插入的元素互换详细见图
不带头节点不带环的单向链表的一些操作(1)_第6张图片

 175 //7.对LinkListInsertBefore进行优化O(n)->O(1)
 176 void LinkListInsertBefore2(LinkNode* pos,LinkNodeType value)
 177 {
 178     if(pos == NULL)
 179     {
 180     //非法输入
 181     return ;
 182     }
 183     LinkNode* new_node=CreateNode(pos->data);
 184     new_node->next=pos->next;
 185     pos->next=new_node;
 186     pos->data=value;
 187     //或者将上面四行代码改为
 188     //LinkListInsert(pos,pos->data);
 189     //pos->data=value;                                                                                     
 190 }


删除pos位置节点

要删除pos位置的节点首先要找到pos节点的位置,那么就要遍历链表,这样这个方法的时间复杂度就为O(n)
 192 //8.删除pos位置节点
 193 void LinkListErase(LinkNode** phead,LinkNode* pos)
 194 {
 195     if(phead == NULL || pos == NULL)
 196     {
 197     //非法输入                                                                                             
 198     return ;
 199     }
 200     if(*phead == NULL)
 201     {
 202     //空链表
 203     return ;
 204     }
 205     LinkNode *cur=*phead;
 206     for(;cur!=NULL;cur=cur->next)
 207     {
 208     if(cur->next==pos)
 209     {
 210         break;
 211     }
 212     }
 213     //循环结束之后要判定是找到了退出还是没找到pos退出
 214     if(cur == NULL)
 215     {
 216     return ;
 217     }
 218     cur->next=pos->next;
 219     DestroyNode(pos);
 220     return ;
 221 }
下面对上面的方法进行优化,将时间复杂度从O(n)变为O(1)

 223 //对LinkListErase进行优化
 224 void LinkListErase2(LinkNode** phead,LinkNode* pos)
 225 {
 226     if(phead == NULL || pos == NULL)
 227     {
 228     //非法输入
 229     return ;
 230     }
 231     if(*phead == NULL)
 232     {
 233     //空链表
 234     return ;
 235     }
 236     if(pos->next == NULL)
 237     {
 238     //要删除的元素为最后一个元素
 239     LinkListPopBack(phead);
 240     return ;
 241     }
 242     pos->data=pos->next->data;
 243     LinkNode* to_erase=pos->next;                                                                          
 244     pos->next=to_erase->next;
 245     DestroyNode(to_erase);
 246 }
查找指定元素的位置

首先要创建一个指针cur指向链表的头部,然后构造一个循环,让如果cur的值就是要找的元素,则返回cur指针的位置,此循环直到cur为NULL时,循环结束

 248 //9.找到节点就返回节点对应的地址,若没找到就返回NULL
 249 LinkNode* LinkListFind(LinkNode* head,LinkNodeType to_find)
 250 {
 251     if(head == NULL)
 252     {
 253     //空链表
 254     return NULL;
 255     }
 256     LinkNode* cur=head;
 257     while(cur!=NULL)
 258     {
 259     if(cur->data==to_find)
 260     {
 261         return cur;
 262     }
 263     cur=cur->next;
 264     }
 265     return NULL;                                                                                           
 266 }

删除指定元素

首先要判断是否合法,和链表是否为空,然后若要删除的元素恰好为第一个元素,则就是头删问题,否则,就需要创建一个指针cur,开始是指向链表的开头,然后让cr指针依次往后面移动,直至cur->next指向要删除的元素,即cur指向要删除的元素的前一个元素,然后方法类似于尾删,具体见下面程序


 268 //10删除指定元素
 269 void LinkListRemove(LinkNode** phead,LinkNodeType to_remove)
 270 {
 271     if(phead == NULL)
 272     {
 273     //非法输入
 274     return ;
 275     }
 276     if(*phead == NULL)
 277     {
 278     //空链表
 279     return ;
 280     }
 281     //删除的元素恰好是第一个
 282     if((*phead)->data==to_remove)
 283     {
 284     LinkNode* to_delete=*phead;
 285     *phead=(*phead)->next;
 286     DestroyNode(to_delete);
 287     return ;
 288     }                                                                                                      
 289     LinkNode* cur=*phead;
 290     for(;cur->next!=NULL;cur=cur->next)
 291     {
 292     if(cur->next->data==to_remove)
 293     {
 294         //cur指向要删除的元素的前一个元素的位置
 295         LinkNode* to_delete=cur->next;
 296         cur->next=to_delete->next;
 297         DestroyNode(to_delete);
 298     }
 299     }
 300     return ;                                                                                               
 301 }

根据值删除所有的元素

首先需要判断链表是否合法和链表是否为空,然后创建一个循环,来找到链表中所有要删除元素的位置,若找到了,则将元素全部删除
 303 //11.删除所有指定的元素
 304 void LinkListRemoveall(LinkNode** phead,LinkNodeType to_remove)
 305 {
 306     if(phead == NULL)
 307     {
 308     //非法输入
 309     return ;
 310     }
 311     if(*phead == NULL)
 312     {
 313     //空链表
 314     return ;
 315     }
 316     while(1)
 317     {
 318     LinkNode* pos=LinkListFind(*phead,to_remove);
 319     if(pos==NULL)
 320     {
 321         //没有找到
 322         return ;
 323     }                                                                                                      
 324     LinkListErase(phead,pos);
 325     }
 326     return ;
 327 }


你可能感兴趣的:(数据结构)