在这里需要强调的是在学习后面内容的同时要把C语言的指针和结构体掌握
链表
特点:
1.一组任意的存储单元存储线性表中的结构
2.这组存储单元可以是连续的,也可以是不连续的
3.每个元素除了存储数据外,还要存储前驱,后继元素的地址
data | address 存储下一个元素或者下一个元素的地址
数据域 | 指针域
单链表:
n个节点按照链式结构存储
每个节点只包含一个指针
例如有三个元素:
头指针pHead指向第一个元素,记录第一个元素的地址 pHead->第一个元素
第一个元素存储第一个数据和指针域指向第二个元素的地址 (第二个元素不必要紧挨着第一个元素)
第二个元素存储第二个数据和指针域指向第三个元素的地址
第三个元素存储第三个数据并且指针域指向空NULL
在链式存储中元素a1分为两个部分:
a1 包括数据部分 通过next指针指向a2的地址
a2 包括数据部分 通过next指针指向a3的地址
a3 包括数据部分 通过next指针NULL (一共三个元素,最后一个元素指向NULL)
最后一个元素中 next 指针域指向NULL
typedef int DataType;
typedef struct Node { //typedef的作用是给结构体取一个别名为Node
DataType data; //之后可以直接用Node定义结构体变量指针 不需要加struct
struct Node* next;
}Node;
上面定义了一个结点,我们在上述概念中提到过,链表由一系列结点构成,每一个结点包括数据和指针,那么以上结构体中我们定义一个结构体表示链表中的一个结点,链表就是通过结构体指针把结点连接起来就形成链表。
//获取指定位置的元素的地址
Node* getptr(Node* head, int pos) //传头结点 传位置
{
Node* p = head; //定义一个指针指向头结点
if (p = NULL || pos == 0) //如果指向节点为空指针代表链表为空或者位置为0
{
return head; //指针为空 返回head则就是返回空 如果是位置0 那么位置0的指着那就是head
}
//链表为空的时候,返回为空,链表部位空的时候返回位置0的指针就是头指针
for (int i = 0; p && i < pos; i++) //检测p是否为空
{
p = p->next; //p的指针指向p所指向的结点的next指针指向的位置
}
return p;
}
我们在上述代码中可以看出来,定义函数时返回时的类型为指针,也就是我们查找到指定位置的元素之后返回值定位置的地址。
下面用动画为大家展示获得指定元素位置的过程:
在用图表示中我们我们在指针位置用^表示指向为NULL
下图中,我们假设要获得第三个元素的地址
主函数需要调用
Node* head = NULL; //最开始赋值为空表示谁都不指向
Node* p = getptr(head,3);// 定义指针接受函数的返回指针
在链表中添加若干个结点(我们在这里添加了四个结点,对应元素分别为10,5,8,3)
这里实现在链表中查找指定位置的元素,所以需要事先添加结点,后面将会有添加结点的方法
图解从for循环开始,第一个if表示如果查找的位置为0,或者头指针没有指向结点,那么就只有一个头结点,这个时候直接返回头节点。
下面表示链表不为空,或者查找的结点位置不是头结点的的情况。
查找指定位置的元素?
int getSize(Node* head)
{
int size = 0;
Node* p = head;
while (p)
{
size++;
p = p->next;
}
return size;
}
主函数调用
getSize(Node *head); //传递链表
获得单链表结点个数?
bool insert(Node** head, int position, DataType d)//insert(&head, 0, 10); 传递指针
//给取Node的地址及就是取Node*的指针
//将0传递给position 将值10传递给DataType
{
if (position<0 || position>getSize(*head)) //如果位置小于0或者大于结点个数的最大值
return false; //返回为假
Node* node = (Node*)malloc(sizeof(Node)); //定义一个结点 分配一个Node大小
node->data = d; //结点中的元素用d赋值给元素的数据域
node->next = NULL; //节点中的元素用NULL赋值给元素的指针域
//在链表第一个结点的前面插入或者在空链表中插入
if (position == 0)
{
node->next = *head;
*head = node;
return true;
}
//在链表的中间和尾部插入结点的情况
Node* p = getptr(*head, position - 1);
Node* r = p->next;
node->next = r;
p->next = node;
return true;
}
在链表头部插入?
在链表中间插入?
链表尾部插入元素?
Bool erase(Node **head ,int pos)
{
If(pos<0||pos>getSize(*head))
{
Return false;
}
Node * p = *head;
If(pos == 0)
{
*head = (*head)->next;
Free(p);
P = NULL;
Return true;
}
P = getSize(*head,position - 1)
Node *q = p->next;
p->next = q->next;
free(q);
q = NULL;
return true;
}
链表删除头结点?
链表删除中间结点?
单链表尾部删除?
Void Union(Node *a,Node *b)
{
Node *p = a;
While(p->next)
P=p->next;
p->next = b;
}
两个链表连接?
void reverse(Node** head) //在完成节点的添加在之后 Head指向了5
{
Node* p = *head; //* *存储的是head的二级地址 所以在赋值之后把head的地址赋值给新定义的指针p
Node* q = p->next; //把p的next赋值给q 及就是把节点p的指针指向q 所以q指向p的下一个结点
if (q = NULL) //如果q为空值 则表示只有一个则表示只有一个结点 那么直接返回
return;
Node* r = q->next; //定义一个指针r 赋值为q的指针
// if (p == *head) //如果p是头指针 开始赋值是已经让指针p指向了head 所以没有必要去判断
//上面一行可以去掉
p->next = NULL; //让头指针的p的指针指向空
while (true)
{
q->next = p; //把p赋值给q的指针 也就是让p指向q的下一个结点
if (r = NULL) //如果r指向为空
{
*head = q; //头指针指向q
break;
}
else //r不指向空
{
p = q; //p指向q
q = r; //q指向r
r = r->next; //r指向r的下一个结点
}
}
}
链表中的结点都被访问一次且只被访问一次的过程
void print(DataType d)
{
printf("%d\n", d);
}
void trave(Node *head,void(*fun)(DataType)) //使用函数指针
{
Node* p = head;
while (p)
{
fun(p->data);
p = p->next;
}
}