2019年8月1日星期四(数据结构)

201981日星期四

. 双向链表与双向循环链表区别?

1. 双向循环链表最后一个节点的后继指向头节点,头节点的前驱指针指向最后一个节点。

2. 节点模型?

struct list_node{

       int a;

       struct list_node*prev;

       struct list_node*next;

}

. 双向循环链表的增删改查?

1.初始化链表头

struct list_node *init_list_head(struct list_node *head)

{

       head = (struct list_node *)malloc(sizeof(struct list_node));

      

       head->prev = head;

       head->next = head;

 

       return head;

}

2.尾插节点

int tail_add_list(struct list_node *head,int num)

{

       struct list_node *Node = NULL;

       struct list_node *p = NULL;

       Node = (struct list_node *)malloc(sizeof(struct list_node));

      

       Node->a = num;

      

       p = head->prev; //p指向最后一个节点

      

       Node->prev = p;

       Node->next = head;

       p->next = Node;

       head->prev = Node;

      

       return 0;

}

3.头插节点

int head_add_list(struct list_node *head,int num)

{

       struct list_node *Node = NULL;

       Node = (struct list_node *)malloc(sizeof(struct list_node));

      

       Node->a = num;

       Node->next = head->next;

       Node->prev = head;

       head->next->prev = Node;

       head->next = Node;

      

       return 0;

}

4.往前遍历链表

int backward_show_list(struct list_node *head)

{

       struct list_node *p = NULL;

       for(p=head->prev;p!=head;p=p->prev)

       {

              printf("p->a = %d\n",p->a);

       }

      

       return 0;

}

5.往后遍历链表

int forward_show_list(struct list_node *head)

{

       struct list_node *p = NULL;

       for(p=head->next;p!=head;p=p->next)

       {

              printf("p->a = %d\n",p->a);

       }

      

       return 0;

}

6.根据特征值搜索节点

void show_node(struct list_node *p)

{

       printf("p->a = %d\n",p->a); 

       return;

}

 

int search_list_node(struct list_node *head,int num)

{

       struct list_node *p = NULL;

       /*

       for(p=head->next;p!=head;p=p->next)

       {

              if(p->a == num)

              {

                     show_node(p);

                     return 0;

              }

       }

       */

      

       for(p=head->prev;p!=head;p=p->prev)

       {

              if(p->a == num)

              {

                     show_node(p);

                     return 0;

              }

       }

      

       return -1;

}

7.根据特征值删除节点

int delete_list_node(struct list_node *head,int num)

{

       struct list_node *p = NULL;

       struct list_node *q = NULL;

      

       for(q=head,p=head->next;p!=head;q=p,p=p->next)

       {

              if(p->a == num)

              {

                     q->next = p->next;

                     p->next->prev = q;

                     free(p);

                    

                     return 0;

              }

       }

      

       return -1;

}

8.删除链表

int delete_list(struct list_node *head)

{

       struct list_node *p = NULL;

       struct list_node *q = NULL;

      

       head->prev->next = NULL;

      

       for(p=q=head;p!=NULL;p=q)

       {

              q = p->next;

              free(p);

       }

      

       return 0;

}

. 双向链表实例。  -> 触摸屏点击切换图片,第一张切换到最后一张,最后一张切换到第一张。

#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <dirent.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <linux/input.h>
#include <strings.h>
 
struct list_node{
       char picname[20];
       struct list_node *next;
       struct list_node *prev;
};
 
struct list_node *init_list_head(struct list_node *head)
{
       head = (struct list_node *)malloc(sizeof(struct list_node));
       
       head->prev = head;
       head->next = head;
       
       return head;
}
 
int tail_add_list(struct list_node *head,char *picname)
{
       struct list_node *Node = NULL;
       struct list_node *p = NULL;
       Node = (struct list_node *)malloc(sizeof(struct list_node));
       
       strcpy(Node->picname,picname);
       
       p = head->prev; //p指向最后一个节点
       
       Node->prev = p;
       Node->next = head;
       p->next = Node;
       head->prev = Node;
       
       return 0;
}
 
 
void show_bmp(char *name)
{
       char bmp_buf[800*480*3];//BMP格式图片缓冲区
       char lcd_buf[800*480*4];//LCD液晶缓冲区
       char show_buf[800*480*4];
       
       int ret,lcd;
       int i,j,x,y;
       
       //1. 访问BMP图片
       FILE *fp = fopen(name,"r");
       if(fp == NULL)
              printf("fopen error!\n");
       
       //2. 跳过BMP图片的54个头数据
       ret = fseek(fp,54,SEEK_SET);
       if(ret != 0)
              printf("fseek error!\n");
       
       //3. 读取BMP图片的数据
       ret = fread(bmp_buf,sizeof(bmp_buf),1,fp);
       if(ret != 1)
              printf("fread error!\n");
       
       //4. 访问LCD液晶
       lcd = open("/dev/fb0",O_WRONLY);
       if(lcd < 0)
              printf("open error!\n");
       
       //5. 像素点赋值
       for(i=0,j=0;i<800*480*4;i+=4,j+=3)
       {
              lcd_buf[i] = bmp_buf[j];
              lcd_buf[i+1] = bmp_buf[j+1];
              lcd_buf[i+2] = bmp_buf[j+2];
              lcd_buf[i+3] = 0;
       }
       
       //6. 上下翻转
       for(y=0;y<480;y++)
       {
              for(x=0;x<800*4;x++)
              {
                     show_buf[(479-y)*800*4+x] = lcd_buf[y*800*4+x];
              }
       }
       
       //7. 将图片数据写入到LCD液晶屏幕上
       ret = write(lcd,show_buf,sizeof(show_buf));
       if(ret != sizeof(show_buf))
              printf("write error!\n");
       
       //8. 关闭设备与文件
       close(lcd);
       fclose(fp);
}
 
int main(int argc,char *argv[])
{
       //0. 初始化链表
       struct list_node *head = NULL;
       head = init_list_head(head);
       
       //1. 打开目录
       DIR *dp = opendir("./pic/");
       if(dp == NULL)
              printf("opendir error!\n");
       
       //2. 切换目录
       chdir("./pic/");
       
       //3. 读取目录中内容
       struct dirent *ep = NULL;
       while(1)
       {
              ep = readdir(dp);
              if(ep == NULL)
                     break;
              
              if(ep->d_name[0] == '.')
                     continue;
              
              tail_add_list(head,ep->d_name);
       }
       
       //4. 显示第一张图片
       struct list_node *p = head->next;
       show_bmp(p->picname);
       
       //5. 访问触摸屏设备
       int fd = open("/dev/input/event0",O_RDONLY);
       if(fd < 0)
              printf("open event0 error!\n");
       
       //6. 不断读取触摸屏数据
       struct input_event buf;
       int x;
       while(1)
       {
              bzero(&buf,sizeof(buf));
              read(fd,&buf,sizeof(buf));
              if(buf.type == EV_ABS && buf.code == ABS_X)
                     x = buf.value;
              
              if(buf.type == EV_KEY && buf.code == BTN_TOUCH && buf.value == 0)
              {
                     if(x < 400) //点击了左边
                     {
                            if(p == head->next)
                            {
                                   p = head->prev;
                            }
                            else{
                                   p = p->prev;
                            }
                            
                            show_bmp(p->picname);
                     }
                     
                     if(x > 400)  //点击了右边
                     {
                            if(p == head->prev)
                            {
                                   p = head->next;
                            }
                            else{
                                   p = p->next;
                            }
                            show_bmp(p->picname);
                     }
              }
       }
       
       close(fd);
       closedir(dp);
       return 0;
}

 

. linux内核链表。

1. 什么是内核链表?

内核链表与传统链表不一样,传统链表的数据域与指针域都是用户自定义,但是内核链表数据域是用户自定义,但指针域是在内核链表的头文件中已经定义好了。

内核链表是一种双向循环,头节点无效的链表。

2. 内核链表模型与传统链表差异?

内核链表头文件: kernel_list.h

既然内核链表指针域已经定义好,那么究竟这个指针域是长什么样子?

该指针域是一个结构体来的:

struct list_head{

      

       struct list_head *next;  -> 后继指针

       struct list_head *prev;  -> 前驱指针         

};

3. 设计内核链表模型?

struct list_node{

       /* 自定义数据域 */

       int a;

       /* 头文件中已经定义好的指针域 */

       struct list_head list;

}

. 内核链表的增删改查?

0. 设计节点

struct list_node{

       int a;

       struct list_head list; //指针域,里面已经包含了两个指针

};  

1. 初始化链表?

1)为头节点申请堆空间。

   head = (struct list_node *)malloc(sizeof(struct list_node));

2)为头节点成员赋值

   INIT_LIST_HEAD(&(head->list));

解析: kernel_list.h 49行:

#define INIT_LIST_HEAD(ptr)

do{

       (ptr)->next = (ptr);    -> ptr应该是"struct list_head *"这种指针类型变量

       (ptr)->prev = (ptr);

}while(0)

2. 尾插数据

1)为新节点申请空间

   Node = (struct list_node *)malloc(sizeof(struct list_node));

2)为新节点数据域赋值

   Node->a = num;

3)调用尾插函数

   list_add_tail(&(Node->list),&(head->list));

解析:

static inline void list_add_tail(struct list_head *new, struct list_head *head)

 

{

       __list_add(new, head->prev, head);

 

}

new:  新节点的指针域的地址

head:  头节点的指针与的地址

__list_add函数例程:

static inline void __list_add(struct list_head *new,

struct list_head *prev,

struct list_head *next)

 

{

      

       next->prev = new;

      

       new->next = next;

      

       new->prev = prev;

      

       prev->next = new;

 

}

3. 头插数据

1)为新节点申请空间

   Node = (struct list_node *)malloc(sizeof(struct list_node));

2)为新节点数据域赋值

   Node->a = num;

3)调用头插函数

   list_add(&(Node->list),&(head->list));

解析:

static inline void list_add(struct list_head *new, struct list_head *head)

 

{

      

       __list_add(new, head, head->next);

 

}

4. 往后遍历链表

===========================================================

功能:list_for_each    -    iterate over a list

解析:

#define list_for_each(pos, head)  -> 这个宏定义就是一个循环来的!

for(pos=(head)->next;pos != (head);pos=pos->next)

  pos: 指向每一个指针域结构体的地址

  head: 头节点指针的地址

功能:list_entry – get the struct for this entry  -> 根据小结构体(指针)地址得到大结构体(数据+指针)的地址

解析:

#define list_entry(ptr, type, member)

((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member)))

  ptr: 指向小结构体的地址的指针变量

  type: 大的结构体的数据类型

  member:在大的结构体中,小的结构体叫什么名字

例子1:20%

int forward_show_list(struct list_node *head)

{

       struct list_head *p = NULL;

       struct list_node *tmp = NULL;

      

       list_for_each(p,&(head->list))

       {

              tmp = list_entry(p,struct list_node,list);

              printf("tmp->a:%d\n",tmp->a);

       }

      

       return 0;

}

 

===========================================================

功能: list_for_each_entry - iterate over list of given type 

       其实就是list_entry和list_for_each的相结合。

解析:

#define list_for_each_entry(pos, head, member)

 

for (pos = list_entry((head)->next, typeof(*pos), member);

&pos->member!=(head);pos=list_entry(pos->member.next, typeof(*pos), member))

  pos: 指向大的结构体的地址指针变量

  head: 头节点的指针域的地址

  member: 在大的结构体中,小的结构体叫什么名字

例子2: 80%

int forward_show_list(struct list_node *head)

{

       struct list_node *p = NULL;

      

       list_for_each_entry(p,&(head->list),list)

       {

              printf("p->a:%d\n",p->a);

       }

      

       return 0;

}

===========================================================

5. 往前遍历链表

功能:list_for_each_prev - iterate over a list backwards

解析:

#define list_for_each_prev(pos, head)

for (pos = (head)->prev; pos != (head);

pos = pos->prev)

  pos: 指向每一个指针域结构体的地址

  head: 头节点指针的地址

例子:

int backward_show_list(struct list_node *head)

{

       struct list_head *p = NULL;

       struct list_node *tmp = NULL;

       list_for_each_prev(p,&(head->list))

       {

              tmp = list_entry(p,struct list_node,list);

              printf("tmp->a:%d\n",tmp->a);

       }

       return 0;

}

===========================================================

   练习2:使用内核链表做通讯录

   要求1: 每一个节点代表一个用户

   要求2: 每一个用户包含姓名,电话号码,学校,序号,关系:  同学classmate  朋友friend  亲人 family

   要求3: 根据序号查找,根据关系来查找。

   要求4: 根据输入序号/姓名删除电话号码

6. 根据特征值来删除链表节点

===========================================================

功能: list_for_each_safe - iterate over a list safe against removal of list entry  -> p和q一起遍历。

解析:

#define list_for_each_safe(pos, n, head)

for (pos = (head)->next, n = pos->next; pos != (head);

pos = n, n = pos->next)

功能: 从链表总删除节点  -> 这个函数仅仅是把节点脱离开链表,并且保证链表不断开,但是内存没有释放。

解析:

static inline void list_del(struct list_head *entry)

{

      

       __list_del(entry->prev, entry->next);

      

       entry->next = (void *) 0;

      

       entry->prev = (void *) 0;

 

}

__list_del函数例程:

static inline void __list_del(struct list_head *prev, struct list_head *next)

 

{

      

       next->prev = prev;

      

       prev->next = next;

 

}

例子1

int delete_list_node(struct list_node *head,int num)

{

       struct list_head *p = NULL;

       struct list_head *q = NULL;

       struct list_node *tmp = NULL;

      

       list_for_each_safe(p,q,&(head->list))

       {

              tmp = list_entry(p,struct list_node,list);

              if(tmp->a == num)

              {

                     list_del(p);  -> p指向需要删除的节点的小结构体

                     free(tmp);     -> tmp指向需要删除的节点的大结构体

              }

       }

       return 0;

}

 

===========================================================

功能:list_for_each_entry_safe – iterate over list of given type safe against removal of list entry

解析:

#define list_for_each_entry_safe(pos, n, head, member)

for (pos = list_entry((head)->next, typeof(*pos), member),

n = list_entry(pos->member.next, typeof(*pos), member);   

     &pos->member != (head);                 

pos = n, n = list_entry(n->member.next, typeof(*n), member))

例子2

int delete_list_node(struct list_node *head,int num)

{

       struct list_node *p = NULL;

       struct list_node *q = NULL;

      

       list_for_each_entry_safe(p,q,&(head->list),list)

       {

              if(p->a == num)

              {

                     list_del(&(p->list));

                     free(p);

              }

       }

      

       return 0;

}

7. 删除整条链表

int delete_list(struct list_node *head)

{

       struct list_node *p = NULL;

       struct list_node *q = NULL;

      

       list_for_each_entry_safe(p,q,&(head->list),list)

       {

              list_del(&(p->list));

              free(p);

       }

       list_del(&(head->list));

       free(head);

      

       return 0;

}

posted @ 2019-08-01 18:02  柚子皮max  阅读(299)  评论(0编辑  收藏  举报