数据结构

- 1 -
1.数据结构体
1.链式结构
通过指针来确定先后顺序的结构
2.线性表
每个节点一定会有一个前驱或一个后驱,不会同时又多个,此时称为线性表
eg:线性表:链表,栈,队列
3.非线性表
每个节点的前驱或后驱不止一个
eg:非线性表:二叉树
4.基本排序算法
所有的线性表或非线性表中的节点都是由结构体组成
设置一个单向链表,保存输入的数值
2.单向链表
每一个节点都只能通过后驱节点来进行查找,不能往前
C语言中,赋值号'='存在左值和右值
其中左值必定为变量
右值可以为变量也可以为常量
在链表
p->next =p->next;
左侧的表示的是结构体指针变量p的成员next
右侧表示的是一个节点
练习:
两个代码:
(1)在siglist2.c文件中,把节点加入到头节点的后面
(2)在siglist2.c文件中,把所有输入的数值按大小排序
3.单向循环链表
循环链表指的是链表中的最后一个节点,它的next指针指向头节点- 2 -
stderr:标准出错 文件描述符数值=2
全缓冲,每次从IO缓冲区取数据不需要‘\n’
stdout:标准输出 文件描述符数值=1
行缓冲,每次从IO缓冲区取数据必须'\n'
练习:
假设班级有34个人,数3退出,求最后留下的那个人学号
1 2 3 4 5 6 --->第一次退出 3 6
1 2 4 5 -->继续数3退出 4 ,再就绪就退出2
学习链表的基础要求:
创建,增,删,查,改
4.双向链表
双向链表就在于任意节点当中,都存在至少两个结构体指针
一个指向前驱,一个指向后驱
练习:
输入的数据按从小到大排列,head节点默认为0
5.内核链表
linux内核中所定义的一种结构链表,
特点:能够连接不同类型的链表
内核链表有固定的参数,必须是内核list.h文件的宏来操作
list.h文件在linux/list.h文件
内核链表之间的联系都是通过struct head_list 来连接的
不管是增删还是查改,都是小结构体在作用
list_add(struct list_head *new, struct list_head *head)
将new节点插入到head节点的后面
list_add_tail(struct list_head *new, struct list_head *head)
将new节点插入到head节点的前面
list_entry(ptr, type, member)
通过小结构体获取大结构体地址
ptr --->小结构体指针
type --->大结构体数据类型
member --->小结构体在大结构体中的变量名
返回值:大结构体的地址- 3 -
晚自习作业:
单链表的逆序
head--1--2--3-4--5--6--7-8--9--
变成head--9--8--7--6--5---4--3--2--1-
1.增
list_add(struct list_head *new, struct list_head *head)
-->将new节点加入到head节点的后面
list_add_tail(struct list_head *new, struct list_head *head)
-->将new节点加入到head节点的前面
2.删
list_del(struct list_head *entry)
-->删除entry节点
3.查
list_for_each(pos, head)
-->pos是临时小结构体指针,head是链表头节点
4.改
list_for_each_safe(pos, n, head)
-->pos和n都是临时小结构体指针,head是链表头节点
小结构体获取大结构体
list_entry(ptr, type, member)
-->通过小结构体获取大结构体地址
ptr --->小结构体指针
type --->大结构体数据类型
member --->小结构体在大结构体中的变量名(不是小结构体指针)
练习:
把数3退出的test1.c改成内核链表
5.list.h头文件
#ifndef __DLIST_H
#define __DLIST_H
/* This file is from Linux Kernel (include/linux/list.h)
* and modified by simply removing hardware prefetching of list items.
* Here by copyright, credits attributed to wherever they belong.
* Kulesh Shanmugasundaram (kulesh [squiggly] isis.poly.edu)
*/
/*
* Simple doubly linked list implementation.
*- 4 -
* Some of the internal functions (“__xxx”) are useful when
* manipulating whole lists rather than single entries, as
* sometimes we already know the next/prev entries and we can
* generate better code by using them directly rather than
* using the generic single-entry routines.
*/
/**
* container_of - cast a member of a structure out to the containing structure
*
* @ptr: the pointer to the member.
* @type: the type of the container struct this is embedded in.
* @member: the name of the member within the struct.
*
*/
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
#define container_of(ptr, type, member) ({ \
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof(type,member) );})
/*
* These are non-NULL pointers that will result in page faults
* under normal circumstances, used to verify that nobody uses
* non-initialized list entries.
*/
#define LIST_POISON1 ((void *) 0x00100100)
#define LIST_POISON2 ((void *) 0x00200)
struct list_head {
struct list_head *next, *prev;
};
//使用小结构体名称 name 进行初始化
//将小结构体的指针指向自己,相当于初始化
#define LIST_HEAD_INIT(name) { &(name), &(name) }
#define LIST_HEAD(name) \
struct list_head name = LIST_HEAD_INIT(name)
//使用小结构体指针 ptr进行初始化
//因为指针只有8字节内存空间,在使用ptr之前,需要先malloc申请内存
#define INIT_LIST_HEAD(ptr) do { \
(ptr)->next = (ptr); (ptr)->prev = (ptr); \
} while (0);
//如果ptr是变量名
// #define INIT_LIST_HEAD(ptr) do { \
// (ptr).next = (&ptr); (ptr).prev = (&ptr); \
// } while (0)
/*
* Insert a new entry between two known consecutive entries.
*
* This is only for internal list manipulation where we know- 5 -
* the prev/next entries already!
*/
//将new节点添加到prev和next节点之间
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;
}
/**
* list_add – add a new entry
* @new: new entry to be added
* @head: list head to add it after
*
* Insert a new entry after the specified head.
* This is good for implementing stacks.
*/
//新节点new插入在head节点后面,也就是整个链表的开头
static inline void list_add(struct list_head *new, struct list_head *head)
{
__list_add(new, head, head->next);
}
/**
* list_add_tail – add a new entry
* @new: new entry to be added
* @head: list head to add it before
*
* Insert a new entry before the specified head.
* This is useful for implementing queues.
*/
//新节点new插入到head节点前面,也就是整个链表的末尾
static inline void list_add_tail(struct list_head *new, struct list_head
*head)
{
__list_add(new, head->prev, head);
}
/*
* Delete a list entry by making the prev/next entries
* point to each other.
*
* This is only for internal list manipulation where we know
* the prev/next entries already!
*/
//将节点prev和next相连接
static inline void __list_del(struct list_head *prev, struct list_head *next)
{
next->prev = prev;
prev->next = next;
}- 6 -
/**
* list_del – deletes entry from list.
* @entry: the element to delete from the list.
* Note: list_empty on entry does not return true after this, the entry is in an
undefined state.
*/
//删除entry节点,entry节点的指针指向NULL
static inline void list_del(struct list_head *entry)
{
__list_del(entry->prev, entry->next);
entry->next = (void *) 0;//NULL
entry->prev = (void *) 0;
}
/**
* list_del_init – deletes entry from list and reinitialize it.
* @entry: the element to delete from the list.
*/
//删除entry节点,entry节点的指针指向自己
static inline void list_del_init(struct list_head *entry)
{
__list_del(entry->prev, entry->next);
INIT_LIST_HEAD(entry);
}
/**
* list_move – delete from one list and add as another’s head
* @list: the entry to move
* @head: the head that will precede our entry
*/
//移动节点,将list节点移动到head节点的后面
static inline void list_move(struct list_head *list,
struct list_head *head)
{
__list_del(list->prev, list->next);
list_add(list, head);
}
/**
* list_move_tail – delete from one list and add as another’s tail
* @list: the entry to move
* @head: the head that will follow our entry
*/
//移动节点,将list节点移动到head节点的前面
static inline void list_move_tail(struct list_head *list,
struct list_head *head)
{
__list_del(list->prev, list->next);
list_add_tail(list, head);
}
/**- 7 -
* list_empty – tests whether a list is empty
* @head: the list to test.
*/
//判断链表是否为空,为空返回1,不为空返回0
static inline int list_empty(struct list_head *head)
{
return head->next == head;
}
//链表剪切
//将整个链表以list为节点,切成两个内核链表
// 原状态为: head----at----.... last----list----first-----....---head
// 切换状态: head---frst---....---head
// at----....---last---at
// 两个链表的联系 list->prev == last , list->next == first
static inline void __list_splice(struct list_head *list,
struct list_head *head)
{
struct list_head *first = list->next;
struct list_head *last = list->prev;
struct list_head *at = head->next;
first->prev = head;
head->next = first;
last->next = at;
at->prev = last;
}
/**
* list_splice – join two lists
* @list: the new list to add.
* @head: the place to add it in the first list.
*/
//判断list链表是否为空,不为空则以list为中心切开成两个链表
static inline void list_splice(struct list_head *list, struct list_head *head)
{
if (!list_empty(list))
__list_splice(list, head);
}
/**
* list_splice_init – join two lists and reinitialise the emptied list.
* @list: the new list to add.
* @head: the place to add it in the first list.
*
* The list at @list is reinitialised
*/
// list切开成两个链表以后,list独立出来,与两个链表无任何联系
static inline void list_splice_init(struct list_head *list,
struct list_head *head)
{
if (!list_empty(list)) {
__list_splice(list, head);- 8 -
INIT_LIST_HEAD(list);
}
}
/**
* list_entry – get the struct for this entry
* @ptr: the &struct list_head pointer.
* @type: the type of the struct this is embedded in.
* @member: the name of the list_struct within the struct.
*/
//小结构体查找大结构体成员
//ptr是小结构体指针
//type是大结构体的数据类型
//member是小结构体的名称
#define list_entry(ptr, type, member) \
((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member)))
/**
* list_for_each - iterate over a list
* @pos: the &struct list_head to use as a loop counter.
* @head: the head for your list.
*/
//使用next指针遍历链表,但不能删除节点
#define list_for_each(pos, head) \
for (pos = (head)->next; pos != (head); \
pos = pos->next)
/**
* list_for_each_prev - iterate over a list backwards
* @pos: the &struct list_head to use as a loop counter.
* @head: the head for your list.
*/
//使用prev指针遍历链表,但不能删除节点
#define list_for_each_prev(pos, head) \
for (pos = (head)->prev; pos != (head); \
pos = pos->prev)
/**
* list_for_each_safe - iterate over a list safe against removal of list
entry
* @pos: the &struct list_head to use as a loop counter.
* @n: another &struct list_head to use as temporary storage
* @head: the head for your list.
*/
//使用next指针遍历链表,可以删除节点
#define list_for_each_safe(pos, n, head) \
for (pos = (head)->next, n = pos->next; pos != (head); \
pos = n, n = pos->next)
/**
* list_for_each_entry - iterate over list of given type
* @pos: the type * to use as a loop counter.
* @head: the head for your list.
* @member: the name of the list_struct within the struct.- 9 -
//list.h头文件
6.栈(先进后出)
栈:一种特殊的线性表
先进后出
以数组形式存在的,称为顺序栈
以链表形式存在的,称为链式栈
栈的结构体
top -->始终指向最新的节点
size-->记录节点的个数
普通节点结构体(存放数据)
链式栈就是一种特殊的单向不循环链表
(1)它的头节点负责记录节点的个数
(2)插入的时候只能进行头插法,也就是只能插入到头节点后面
(3)删除的时候,只能够进行头部删除,也就是只能从头节点
后面开始删除
7.队列(先进先出)
队列:一种特殊的线性表
先进先出
*/
#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))
/**
* list_for_each_entry_safe – iterate over list of given type safe against
removal of list entry
* @pos: the type * to use as a loop counter.
* @n: another type * to use as temporary storage
* @head: the head for your list.
* @member: the name of the list_struct within the struct.
*/ 
#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))
#endif
}- 10 -
队列结构体:
front -->始终指向最后加入的节点
rear -->始终指向最早加入的节点
size -->节点个数
链式队列就是一种特殊的单向不循环链表
(1)它的头节点记录节点的个数
(2)插入的时候是尾部插入
(3)删除的时候是头部删除
练习:
使用目录检索当前文件夹下的文件名,并且分别用栈,队列来打印文件名
8.非线性表
数据结构中的节点不止一个后驱或一个前驱,这时称为是非线性表
1.树
在整个存储的数据结构中,存在一个"根"节点,其他所有的节点都是
这个节点的后继节点。
2.关键概念
双亲与孩子:
后继节点是其前驱节点的孩子
前驱节点是后继节点的双亲
兄弟:
具有相同双亲节点的节点互为兄弟节点
节点的层次:
一般以根节点的层次设为1,然后往下生成一层节点,层次加1
节点的度:
当前节点的后继节点的个数
9.二叉树
每个节点的度不超过2的一组有序的树- 11 -
有序:每一个节点都有严格排序
完全二叉树:
设有H层二叉树,那么除了第H层和H-1层以外的节点的度都是2
并且第H层的节点从左到右连续排序。
平衡二叉树:
设有H层节点,总结点个数为 2^H -1 个
也就是除了第H层以外,其余层的度都是2,第H层的节点都有
二叉树遍历:
前序遍历: 根节点---左节点---右节点
中序遍历: 左节点---根节点---右节点
----就是将二叉树按从小到大排列
后序遍历: 左节点---右节点---根节点
按层遍历: 从根节点开始,从左往右打印每一层的节点
重点:
二叉树,完全二叉树,平衡二叉树的概念
中序遍历,按层遍历
10.排序算法
当前讨论从小到大排列:
1.直接插入排序
每次取一个数字key,将这个数字与前面的所有数字做比较,发现前面的数字比key值小,
就将被比较的数字往后移动一位,前面的数字比key值大,将key放入到被比较数字的
后面一位,如果所有数字都比key值小,那么就把数字key放入到开头位置
2.冒泡排序
每次只比较相邻的两个数字,每次前者比后者大的时候,就交换位置,否则不交换
直到第一轮比较完成,最大的数字会放在数列的末尾
3.选择排序
事先设置一个最小值min,并且将这个数值后的所有数字都与它作比较,如果发现有
比min小的数值,就交换两个,否则继续往后做比较
4.快速排序
设定一个键值key,将比key小的全部放在key的左侧,比key大的全部放在key的右侧
然后递归分别对key的左右两侧都进行排序
#include <stdlib.h>
void qsort(void base, size_t nmemb, size_t size,
int(compar)(const void *, const void *));
快速排序的函数
void base --->被快排的数组
size_t nmemb --->被快排的数组的数字个数
size_t size --->这个数组的数据类型占用字节的大小
int(compar)(const void *, const void *) --->自定义函数,根据该函数的正负来确定- 12 -
是从小到大还是从大到小
练习:
创建一个双向循环链表,分别放入0~9的数值,然后分别
用直接插入,选择,冒泡,快速排序对其进行操作
-->希尔排序,归并排序

posted on 2020-08-26 15:16  Qianer  阅读(91)  评论(0)    收藏  举报

导航