数据结构笔记三



 
链表是 一种常见的线性数据结构,没有特殊要求的链式表。

 存放数据的基本单位也是节点。

 地址  不连续的节点  序列,彼此通过  指针 相互连接。
 
  单向线性链表:
  单向循环链表
  双向线性
  双线循环
  数组链表
  链表数组
  二维链表
 

数组链表---元素是数组的链表
struct Node{
 int data[5];
 struct Node* next;
}; 

链表数组---元素是链表的数组

struct Linked{};
struct Linked arr[5];


最常见的链表就是 单向线性链表和双向线性链表

写一个双向链表:

节点:

 struct ListNode{
  int data;
  struct ListNode* next;//下一个节点
  struct ListNode* prev;//上一个节点
 };

链表: 
 Struct List{
  struct ListNode* head;//链表头
  struct ListNode* tail;//链表尾,方便追加
  struct ListNode* frwd;//从前向后查找的位置,和链表头不一样,
  //在查找时frwd是可以改变。
  //正向迭代指针,提供遍历。
  struct ListNode* bkwd;//从后向前查找的位置
};

遍历:从头到尾找一遍 
迭代:一个挨着一个 进行遍历 

特点:
 第一个没有前指针
 最后一个没有后指针
 当然,可以在前面加一个,也可以在后面加一个
 可以有下标,一个挨着一个
 链表增删方便,随机访问不方便 
 

增删元素的函数:
 追加元素---修改原来的tail的next指向新节点,链表的tail也指向新节点。
 插入元素---新节点前的节点改next,新节点后的节点改prev,新节点自身的前后设置好
 删除元素---删除节点前的节点改next,删除节点后的节点改prev,把删除节点内存回收。
 
 
 返回数据的两种方式:
 
 1、 用return语句返回数据
 
 
 2、 用 指针做参数 传入数据的地址,把 数据 带回来。
  
------------------------------------------------------------------------


  1 #ifndef _LS_H
  2 #define _LS_H
  3 #include <sys/types.h>
  4 typedef struct ListNode{//双向链表节点
  5     int data;//数据
  6     struct ListNode* next;//后节点
  7     struct ListNode* prev;//前节点
  8 }LISTNODE;
  9 typedef struct List{
 10     LISTNODE* head;//头节点
 11     LISTNODE* tail;//尾节点
 12     LISTNODE* frwd;//正向查询 迭代节点
 13     LISTNODE* bkwd;//反向查询 迭代节点(暂时不用)
 14 }LIST;
 15 void list_init(LIST* list);//初始化 空链表
 16 void list_deinit(LIST* list);//释放所有的链表 回空
 17 int list_empty(LIST* list);//判断是否空
 18 void list_append(LIST* list,int data);//追加元素
 19 //前插(pos之前)
 20 int list_insert(LIST* list,size_t pos,int data);//插入元素
 21 int list_erase(LIST* list,size_t pos);//按位置删除
 22 void list_remove(LIST* list,int data);//按数据删除
 23 int * list_at(LIST* list,size_t pos);//按位置取数,不删除
 24 void list_clear(LIST* list);//清空所有元素
 25 size_t list_size(LIST* list);//元素个数
 26 //设计一下正向迭代算法(提高取数据的效率)
 27 void list_begin(LIST* list);//回头,开始正向迭代(从头开始)
 28 int * list_next(LIST* list);//向next移动一下并返回移动之前的数据
 29 int * list_prev(LIST* list);//向prev移动一下并返回移动之前的数据
 30 int * list_current(LIST* list);//不移动 直接取数据
 31 int list_end(LIST* list);//判断是否迭代结束
 32 #endif
 
 
---------------------------------------------------------------------------
ls.c 

  1 #include <stdlib.h>
  2 #include "ls.h"
  3 
  4 static LIST_NODE * create_node(int data,
  5         LIST_NODE* prev,LIST_NODE* next){
  6     LIST_NODE* node = malloc(sizeof(LIST_NODE));
  7     node->data = data;
  8     node->prev = prev;
  9     node->next = next;
 10     return node;
 11 }//回收节点时,返回前节点和后节点指针
 12 static LIST_NODE* destroy_node(LIST_NODE* node,LIST_NODE** prev){
 13     //next被return,prev用2级指针
 14     LIST_NODE* next = node->next;
 15     if (prev) *prev = node->prev;//prev不为空带出前节点
	free(node);
 16     return next;
 17 }
 18 void list_init(LIST * list){//初始化 空链表
 19     list->head = NULL;
 20     list->tail = NULL;
 21 }
 22 void list_deinit(LIST * list){//释放所有的链表 回空
 23     while(list->head)//从前向后删除,不需要前节点信息,所有第二个参数为NULL
 24     {
 25         list->head = destroy_node(list->head,NULL);
 26     }
 27     list->tail = NULL;
 28 }
 29 int list_empty(LIST* list){//判断是否空
 30     return (!list->head) && (!list->tail);
 31 }
 32 void list_append(LIST* list,int data){//追加元素
 33     list->tail = create_node(data,list->tail,NULL);
 34     //list->tail链表指向新节点
 35     //list->tail->prev就是原来链表的最后节点
 36     if(list->tail->prev)//非空
 37         list->tail->prev->next = list->tail;
 38     else//空的话,表示没有节点,则改变链表的头节点
 39         list->head = list->tail;
 40 
 41 }
 42 //前插元素(pos之前)
 43 int list_insert(LIST* list,size_t pos,int data){//插入元素
 44     LIST_NODE* find = NULL;//head和tail不变,find可变;
 45     //因为head不能动,只能用find来动来进行遍历
 46     for (find=list->head;find;find=find->next)
 47     {//从前向后开始走,一步一步往下走,直到find为空(大范围操作)
 48         if (!pos--){//找到位置,pos=0,建节点;
 49             //如果pos参数为10的话,在这里find需要走11次才能插入
 50             //找到位置后,建立节点
 51             LIST_NODE* node = create_node(data,find->prev,find);
 52             //找到find位置,则数据为data,find->prev前节点,find后节点
 53             if (node->prev) //node->prev不为空,则不是插入第一个位置
 54             {
 55                 node->prev->next = node;
 56             }else//为空,则将list-head设置为新节点即可。
 57                 list->head = node;
 58             node->next->prev = node;//改的后节点
 59             return 1;//代表插入成功
 60         }
 61     }
 62     return 0;//代表插入失败
 63 }
 64 int list_erase(LIST* list,size_t pos){//按位置删除
 65     LIST_NODE * find = NULL;
 66     for (find=list->head;find;find=find->next)
 67     {
 68         if (!pos--)
 69         {
 70             LIST_NODE * prev = NULL;
 71             LIST_NODE * next =destroy_node(find,&prev);
 72             if (prev)//判断不是头节点
 73                 prev->next = next;//改变前节点的next;
 74             else//如果删除的是头节点
 75                 list->head = next;
 76             if (next)//判断不是尾节点
 77                 next->prev = prev;
 78             else
 79                 list->tail = prev;
 80             return 1;
 81         }
 82     }
 83     return 0;
 84 }
 85 void list_remove(LIST* list,int data){//按数据删除
 86     LIST_NODE * find = NULL, *findnext = NULL;
 87     for (find=list->head;find;find=findnext)
 88     {
 89         findnext=find->next;//先保存,否则销毁find就执行不下去了。
 90         if (find->data == data)//找到了数据
 91         {
 92             LIST_NODE* prev = NULL;
 93             LIST_NODE* next = destroy_node(find,&prev);
 94             if (prev)  prev->next = next;
 95             else list->head = next;
 96             if (next)  next->prev = prev;
 97             else list->tail = prev;
 98         }
 99     }
100 }
101 //直接返回int,就没法确定是不是 找到的数据了。返回的0是数值还是位置呢。
102 int * list_at(LIST* list,size_t pos){//按位置取数,不删除
103     //为什么这里是int *,而不是int
104     //如果是int,万一提供的pos越界了,则NULL。
105     LIST_NODE * find = NULL;
106     for(find=list->head;find;find=find->next)
107     {
108         if (!pos--)
109         {
110             return &(find->data);
111         }
112     }
113     return NULL;//NULL可以代表没有找到。
114 }
115 void list_clear(LIST* list){//清空所有元素
116     list_deinit(list);
117 }
118 size_t list_size(LIST* list){//元素个数
119     size_t size = 0;
120     LIST_NODE * node = NULL;
121     for (node=list->head;node;node=node->next)
122     {
123         size++;
124     }
125     return size;
126 }
127 void list_begin(LIST* list){//开始正向迭代
128     list->frwd=list->head;//从头开始
129 }
130 int * list_next(LIST* list){//向next移动了一下
131     int * data = &(list->frwd->data);
132     list->frwd = list->frwd->next;
133     return data;
134 }
135 int * list_prev(LIST* list){//向prev移动了一下
136     int * data = &(list->frwd->data);
137     list->frwd = list->frwd->prev;
138     return data;
139 }
140 int * list_current(LIST* list){//不移动,直接取当前数据
141     return &(list->frwd->data);
142 }
143 int  list_end(LIST* list){//判断是否结束
144     return !list->frwd;
145 }
                 
------------------------------------------------------------------------
 测试用例代码:
  1 #include <stdio.h>
  2 #include "ls.h"
  3 #include <stdlib.h>
  4 int main()
  5 {
  6     LIST list;
  7     list_init(&list);
  8     list_append(&list,10);list_append(&list,30);list_append(&list,50);
  9     list_insert(&list,1,20);list_insert(&list,3,40);
 10     printf("size=%d\n",list_size(&list));
 11     int i;//这种查找方式 效率 问题很大,每次都从头开始。
 12     for(i=0;i<list_size(&list);i++)
 13         printf("%d\n",*(list_at(&list,i)));
 14     list_erase(&list,1);//删除了20
 15     printf("删除了20------------------------------\n");
 16     for(i=0;i<list_size(&list);i++)
 17         printf("%d\n",*(list_at(&list,i)));
 18     printf("追加40------------------------------------\n");
 19     list_append(&list,40);
 20     for(i=0;i<list_size(&list);i++)
 21         printf("%d\n",*(list_at(&list,i)));
 22     printf("找到40删除所有的40----------------------------------\n");
 23     list_remove(&list,40);//应该删除2个40
 24     
 25     for(i=0;i<list_size(&list);i++)
 26         printf("%d\n",*(list_at(&list,i)));
 27     list_deinit(&list);
 28     return 0;
 29 }  
 
 ---------------------------------------------------------------------
songxl@ubuntu:~/ds/day03$ gcc testls.c ls.c 
songxl@ubuntu:~/ds/day03$ a.out
size=5
10
20
30
40
50
删除了20------------------------------
10
30
40
50
追加40------------------------------------
10
30
40
50
40
找到40删除所有的40----------------------------------
10
30
50

-----------------------------------------------------------------
测试用例2 
  1 #include <stdio.h>
  2 #include "ls.h"
  3 #include <stdlib.h>
  4 int main()
  5 {
  6     LIST list;
  7     list_init(&list);
  8     list_append(&list,10);list_append(&list,30);list_append(&list,50);
  9     list_insert(&list,1,20);list_insert(&list,3,40);
 10     printf("size=%d\n",list_size(&list));
 11     list_begin(&list);//先从头开始
 12     while(!list_end(&list))//不到尾节点就循环
 13         printf("%d\n",*(list_next(&list)));
 14     list_erase(&list,1);//删除了20
 15     printf("删除了20------------------------------\n");
 16     list_begin(&list);//先从头开始
 17     while(!list_end(&list))//不到尾节点就循环
 18         printf("%d\n",*(list_next(&list)));
 19     printf("追加40------------------------------------\n");
 20     list_append(&list,40);
 21     printf("找到40删除所有的40----------------------------------\n");
 22     list_remove(&list,40);//应该删除2个40
 23     
 24     list_begin(&list);//每次迭代都要先从头开始
 25     while(!list_end(&list))//不到尾节点就循环
 26         printf("%d\n",*(list_next(&list)));
 27     list_deinit(&list);
 28     return 0;
 29 }  
 
 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值