c/c++ 線性表之單向連結串列

小石王發表於2018-06-23

c/c++ 線性表之單向連結串列

線性表之單向連結串列

不是存放在連續的記憶體空間,連結串列中的每個節點的next都指向下一個節點,最後一個節點的下一個節點是NULL。

真實的第一個節點是頭節點,頭節點不存放資料,單純為了編寫程式方便。但是下面註釋裡寫的【第一個節點】的含義是頭節點的下一節點,也就是真實存放資料的第一個節點。

下面的程式碼實現了以下功能

函式 功能描述
push_back 從連結串列的最後插入節點
push_front 從連結串列的起始插入節點
show_list 列印出連結串列裡每個節點的值
pop_back 刪除連結串列最後一個節點
pop_front 刪除連結串列起始節點
insert_val 在合適的位置插入一個節點;
比如原來的連結串列:1->3->NULL,當要插入的節點的值為2的時候,就會在1和3之間插入這個節點,插入後的連結串列:1->2->3->NULL
find 查詢指定的節點
length 返回連結串列中節點的個數
delete_val 刪除指定的節點
sort by val 排序,改變節點裡的值,不改變節點之間的鏈條
sort by node 排序,重新排列節點
resver back 按倒序,重新排列節點(實現方法是:尾插)
resver front 按倒序,重新排列節點(實現方法是:頭插)
clear 釋放除了頭節點之外的所有節點所佔用的記憶體空間
destroy 釋放所有節點的所佔用的記憶體空間,包括頭節點

seqnode.h

#ifndef __SEQNODE__
#define __SEQNODE__

#include <stdio.h>
#include <malloc.h>
#include <assert.h>
#include <memory.h>
#include <stdbool.h>

#define ElemType int

//Node代表節點,data是節點裡儲存的資料,next指標儲存下一個節點的地址
typedef struct Node{
  ElemType data;
  struct Node* next;
}Node;

//NodeList代表連結串列,first指向頭節點,last指向最後一個節點,size是連結串列裡節點的個數
typedef struct NodeList{
  Node*  first;
  Node*  last;
  size_t size;
}NodeList;

void init(NodeList*);
void push_back(NodeList*, ElemType);
void push_front(NodeList*, ElemType);
void pop_back(NodeList*);
void pop_front(NodeList*);
void show_list(NodeList*);
void insert_val(NodeList*, ElemType);
Node* find(NodeList*, ElemType);
void delete_val(NodeList*, ElemType);
void sort(NodeList*);
void sort1(NodeList*);
void resver(NodeList*);
void resver1(NodeList*);
void resver2(NodeList*);
void clear(NodeList*);
void destroy(NodeList*);

#endif

seqnode.c

#include "seqnode.h"

//分配頭節點的記憶體空間
void init(NodeList* list){
  list->first = (Node*)malloc(sizeof(Node));
  list->last = list->first;
  list->first->next = NULL;
  list->size = 0;
}

//從連結串列的最後插入節點
void push_back(NodeList* list, ElemType val){
  Node* p = (Node*)malloc(sizeof(Node));
  assert(NULL != p);
  p->data = val;
  p->next = NULL;

  list->last->next = p;
  list->last = p;
  list->size++;
}

void push_front(NodeList* list, ElemType val){
  Node* p = (Node*)malloc(sizeof(Node));
  p->data = val;
  
  //新插入節點的下一個節點指向原來連結串列中的第一個節點
  p->next = list->first->next;
  //頭節點的next指向新插入的節點
  list->first->next = p;
  //如果是插入節點前的連結串列裡沒有如何節點,則必須要把last指向插入的節點
  if(list->size == 0){
    list->last = p;
  }
  list->size++;
}

void show_list(NodeList* list){
  Node* tmp = list->first->next;
  while(tmp != NULL){
    printf("%d->", tmp->data);
    tmp = tmp->next;
  }
  printf("NULL
");
}

//刪除最後的節點
void pop_back(NodeList* list){
  if(list->size == 0)return;
  Node* p = list->first;
  //尋找最後節點的前一個節點,當p->next == list->last,p就是最後節點的前一個節點。
  while(p->next != list->last){
    p = p->next;
  }
  //釋放最後節點所佔用的空間
  free(list->last);
  //p變成最後節點
  list->last = p;
  p->next = NULL;
  list->size--;
}

//刪除第一個的節點
void pop_front(NodeList* list){
  if(list->size == 0)return;
  //p就是第一個節點
  Node* p = list->first->next;
  //把第二個節點變成第一個節點
  list->first->next = p->next;
  //如果連結串列裡只有一個節點,則必須移動last
  if(list->size == 1){
    list->last = list->first;
  }
  list->size--;
  //釋放第一個節點所佔用的空間
  free(p);
}

//在合適的位置插入一個節點.
//比如原來的連結串列:1->3->NULL,當要插入的節點的值為2的時候,就會在1和3之間插入這個節點,插入後的連結串列:1->2->3->NULL
void insert_val(NodeList* list, ElemType val){
  //如果連結串列為空直接呼叫尾插
  if(list->size == 0){
    push_back(list, val);
    return;
  }
  Node* p = (Node*)malloc(sizeof(Node));
  p->data = val;
  Node* t = list->first;
  do{
    //t->next不是最後一個節點,並且在合適位置
    if(val >= t->data && t->next != NULL && val <= t->next->data){
      p->next = t->next;
      t->next = p;
      break;
    }
    //t->next是最後一個節點
    if(t->next == NULL){
      list->last->next = p;
      list->last = p;
      list->last->next = NULL;
      break;
    }
    t = t->next;
  }
  while(1);
  list->size++;
}
Node* find(NodeList* list, ElemType val){
  if(0 == list->size){
    return NULL;
  }
  Node* p = list->first->next;
  do{
    if(val == p->data){
      return p;
      break;
    }
    p = p->next;
  }
  while(NULL != p);
}
void delete_val(NodeList* list, ElemType val){
  if(0 == list->size)return;
  Node* p = list->first;
  do{
    if(p->next->data == val){
      //p->next是最後一個節點,所以必須移動last的指向
      if(NULL == p->next->next){
        list->last = p;
      }
      free(p->next);
      //p->next是要被刪除的節點,所以p->next指向要被刪除節點的下一個節點
      p->next = p->next->next;
      list->size--;
      break;
    }
    p = p->next;
  }while(NULL != p->next);
}

//利用find函式進行刪除
void delete_val1(NodeList* list, ElemType val){
  if(0 == list->size)return;
  Node* p = find(list, val);
  if(NULL == p)return;
  //如果要被刪除的節點是最後一個節點,就直接呼叫尾刪。
  if(p == list->last){
    pop_back(list);
  }
  //find找到是要被刪除的節點,但是不知道它前面的節點的地址,所以就不無法讓它前面的節點的next指向它後面的節點
  //解決辦法,把它後節點裡的資料,賦給它,然後刪除它後面的節點。如果它後面的節點是最後節點,必須修改last的指向。
  else{
    p->data = p->next->data;
    free(p->next);
    p->next = p->next->next;
    if(NULL == p->next){
      list->last = p;
    }
    list->size--;
  }
}
//不重新排列節點,只是修改節點裡的值,用冒泡法排序。
void sort(NodeList* list){
  if(list->size == 0 || list->size == 1)return;
  Node* p = list->first->next;
  for(int i = 0; i < list->size-1; ++i){
    for(int j = 0; j < list->size-i-1; ++j){
      if(p->data > p->next->data){
        p->data = p->data + p->next->data;
        p->next->data = p->data - p->next->data;
        p->data = p->data - p->next->data;
      }
      p = p->next;
    }
    p = list->first->next;
  }
}

void insert_pnt(NodeList* list, Node* node){
  Node* t = list->first;
  do{
    if(t->next != NULL && node->data <= t->next->data){
      node->next = t->next;
      t->next = node;
      break;
    }
    if(t->next == NULL){
      list->last->next = node;
      list->last = node;
      list->last->next = NULL;
      break;
    }
    t = t->next;
  }
  while(1);
  list->size++;
}

//重新排列節點。思路:把連結串列分成2個連結串列,第一個連結串列留一個節點,利用insert_val,把剩下的節點再插回第一個節點
void sort1(NodeList* list){
  if(list->size == 0 || list->size == 1)return;

  list->size = 1;
  list->last = list->first->next;
  list->last->next = NULL;
    
  //n指向第二個節點
  Node* n = list->first->next->next;
  Node* t;
  while(NULL != n){
    //因為n>next在下面的insert_pnt裡會被改變,所以提前把n->next方到t裡儲存
    t = n->next;
    insert_pnt(list, n);
    n = t;
  }
}
void push_back_pnt(NodeList* list, Node* node){
  list->last->next = node;
  list->last = node;
  list->last->next = NULL;
  list->size++;
}
//思路:把連結串列分成2個連結串列,第一個連結串列只有頭幾點,剩下的節點放在第二個連結串列,迴圈找第二個連結串列裡的尾節點,利用尾插,把找到的尾節點插入回第一個連結串列。
void resver(NodeList* list){
  if(list->size == 0 || list->size == 1)return;

  Node* e = list->last;
  Node* b = list->first->next;
  Node* tmp = list->first;
  size_t sz = list->size;

  list->last = list->first;
  list->size = 0;

  while(sz-- > 0){
    //尋找最後一個節點,找到後修改e,讓e為往前移動一個節點
    while(tmp->next != e && b != e){
      tmp = tmp->next;
    }
    if(b == e){
      push_back_pnt(list, b);
    }else{
      push_back_pnt(list, tmp->next);
    }
    //讓e為往前移動一個節點
    e   = tmp;
    //讓tmp再次指向第一個節點,目的是再從第一個節點開始,去尋找最後一個節點
    tmp = b;
  }
}

void push_front_pnt(NodeList* list, Node* node){
  node->next = list->first->next;
  list->first->next = node;
  list->size++;
}
//思路:把連結串列分成2個連結串列,第一個連結串列只有第一個節點,剩下的節點放在第二個連結串列,利用頭插,把第二個連結串列裡的節點再插入回第一個連結串列。
void resver1(NodeList* list){
  if(list->size == 0 || list->size == 1)return;

  Node* head = list->first->next->next;

  list->last = list->first->next;
  list->last->next = NULL;
  list->size = 1;

  Node* tmp;
  while(head != NULL){
    tmp = head->next;
    push_front_pnt(list, head);
    head = tmp;
  }
}
//和resver1的思路一樣,但不呼叫push_front_pnt
void resver2(NodeList* list){
  if(list->size == 0 || list->size == 1)return;

  Node* p = list->first->next->next;
  list->last = list->first->next;
  list->last->next = NULL;

  Node* q;
  while(p != NULL){
    q = p->next;
    p->next = list->first->next;
    list->first->next = p;
    p = q;
  }
}

void clear(NodeList* list){
  if(list->size == 0) return;
  Node* b = list->first->next;
  Node* q;
  while(b != NULL){
    q = b->next;
    free(b);
    b = q;
  }
  list->last = list->first;
  list->last->next = NULL;
  list->size = 0;
}

void destroy(NodeList* list){
  Node* b = list->first;
  Node* q;
  while(b != NULL){
    q = b->next;
    free(b);
    b = q;
  }
}

seqnodemain.c

#include "seqnode.h"

int main(){
  NodeList list;
  init(&list);
  int select = 1;
  ElemType item;
  Node* node = NULL;
  while(select){
    printf("*****************************************
");
    printf("*** [1]   push_back   [2]  push_front ***
");
    printf("*** [3]   show_list   [4]  pop_back   ***
");
    printf("*** [5]   pop_front   [6]  insert_val ***
");
    printf("*** [7]   find        [8]  length     ***
");
    printf("*** [9]   delete_val  [10] sort by val***
");
    printf("*** [11]  sort by node[12] resver back***
");
    printf("*** [13]  resver front[14] clear      ***
");
    printf("*** [0]   quit        [15*]destroy    ***
");
    printf("*****************************************
");
    printf("請選擇:>");
    scanf("%d", &select);
    if(0 == select)
      break;
    switch(select){
    case 1:
      printf("請輸入要插入的資料,以-1結束>
");
      while(scanf("%d",&item) && item != -1){
    push_back(&list, item);
      }
      show_list(&list);
      break;
    case 2:
      printf("請輸入要插入的資料,以-1結束>
");
      while(scanf("%d", &item) && item != -1){
    push_front(&list, item);
      }
      show_list(&list);
      break;
    case 3:
      show_list(&list);
      break;
    case 4:
      pop_back(&list);
      show_list(&list);
      break;
    case 5:
      pop_front(&list);
      show_list(&list);
      break;
    case 6:
      printf("請輸入要插入的資料>
");
      scanf("%d",&item);
      insert_val(&list, item);
      show_list(&list);
      break;
    case 7:
      printf("please enter what you shoule find out>
");
      scanf("%d",&item);
      node = find(&list, item);
      if(node == NULL){
    printf("can not find %d
", item);
      }
      break;
    case 8:
      printf("length is %ld
", list.size);
      break;
    case 9:
      printf("please enter what you want to delete>
");
      scanf("%d",&item);      
      delete_val(&list, item);
      show_list(&list);
      break;
    case 10:
      sort(&list);
      show_list(&list);
      break;
    case 11:
      sort1(&list);
      show_list(&list);
      break;
    case 12:
      resver(&list);
      show_list(&list);
      break;
    case 13:
      resver2(&list);
      show_list(&list);
      break;
    case 14:
      clear(&list);
      show_list(&list);
      break;
      //case 15:
      //destroy(&list);
      break;
    default:
      break;
    }
  }

  destroy(&list);
}

相關文章