在单链表里面,每个节点包含一个指向下一个节点 的指针。链表的最后一个节点的指针字段是一个值为NULL的指针,他的作用就是提示链表后面不再有其他的节点。在你找到链表的第一个节点的时候调用节点里面的指针就可以依次访问剩下所有的节点。为了记住链表的起始位置,可以用一个根根指针。根指针指向链表的第一个节点,注意这里根指针只是一个指针,它不包含其他的任何数据。它的作用仅仅就是指向链表的开始节点,调用根指针就可以访问链表的节点。
struct Stu
{
char data[100];
struct Stu *next; //定义一个指向下一个节点的指针
};
在单链表里面,当每次创建一个节点的时候,可以将数据存储在节点的数据域,但是同时还需要将创建的新节点的地址赋给上个节点的next指针和将新节点的next指针置为NULL。
单链表的创建:
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <stdlib.h>
typedef struct Node
{
char data;
struct Node *next;
}Node;
int main()
{
char ch = 0;
Node *root = NULL;
Node *p2 = NULL;
Node* p1 = NULL;
while (ch = getchar())
{
p1 = malloc(sizeof(Node)); //Set up a new node
if (p1 == NULL)
{
return ;
}
if (root == NULL)
{
p1->data = ch; //put ch to data
root = p1; // put address to root
p1->next = NULL; //set next
}
else
{
p2->next = p1;
p2 = p1; //...
p1->data = ch;
}
}
free(p1);
return 0;
}
注释:在main函数里面,先创建了root指针当创建的第一个节点时候,将第一个节点的地址赋给root,当创建的新节点不是第一个节点的时候,经过if语句的判断,将新节点的地址赋给上一个节点的next指针,之后又将新节点p1赋给p2,当程序创建第二个节点的时候又可以将新创建的节点的地址重新赋给上一个节点。这样重复创建创建链表的节点,同时也将我们要存储的数据存储到每个节点 的数据区。
链表的插入
链表的插入又可以分为两类:链表的前面插入和后面插入
链表的后面插入:
void sll_list(Node **head,int num)
{
Node *p = *head;
Node *new = malloc(sizeof(Node)); //创建一个新节点
if (new == NULL)
{
return ; //判断新节点是否创建成功
}
new->data = num; //将要加入的数据赋值给数据区
new->next = NULL;
if (*head == NULL)
{
*head = new; //当头指针为空时直接将新节点的地址赋给头指针
}
else
{
while(p->next)
{
p = p->next; //找到链表的尾节点
}
p->next = new; //将新节点作为为节点
}
}
链表的前面插入:
void sll_list(Node **head,int num)
{
Node*new = malloc(sizeof(Node));
if (new == NULL)
{
return ;
}
new->data = num;
new->next = NULL;
if (*head == NULL)
{
*head = new;
}
else
{
new->next = *head; //将新节点作为链表的第一个节点
*head = new;
}
}
在链表里面删除数据:
void All_detele_List(Node **head)
{
Node *p = *head;
while (p)
{
free(p);
p = p->next;
}
}
在链表里面指定修改的数据:
void change_list(Node **head,int aim,int num)
{
Node *tmp = NULL;
Node *p = *head;
assert(*head);
while (p)
{
if (p->data == aim) //在链表里面找到数据所在的位置
{
p->data = num;
}
p = p->next;
}
}
在链表里面删除所有的节点:
void SL_Clear(Node **p_list)
{
Node* p = NULL;
Node* q = NULL;
p = *p_list;
while(p!=NULL)
{
q = p->next;//借助于q存储p的链域,否则释放p后无法引用
free(p);
p = q;
}
*p_list = NULL;//将所指的链表指针设为NULL
}
注意:这里一定要free掉每个节点的空间,不然会出现内存泄露的问题
计算链表里面的节点个数:
int return_number(Node ** head)
{
int i = 0; //定义一个计数器
Node *p = *head;
assert(*head);
while (p) //当p不为null的时候计数器加1
{
p = p->next;
i++;
}
return i;
}
单链表的经典笔试题:
题目:输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针指向任意一个节点),返回结果为复制后复杂链表的head。
方法1:链表遍历一遍,key和value的值对应关系如下然后再遍历一次链表,设置复制链表的next和random指针
copy_list(RandomListNode pHead)
{
HashMap<RandomListNode, RandomListNode> map=new HashMap<RandomListNode, RandomListNode>();
RandomListNode cur=pHead;
while(cur!=null){
map.put(cur, new RandomListNode(cur.label));
cur=cur.next;
}
cur=pHead;
while(cur!=null){
map.get(cur).next=map.get(cur.next);
map.get(cur).random=map.get(cur.random);
cur=cur.next;
}
return map.get(pHead);
}
方法2:遍历一次链表,将链表复制的副本节点放到自己后面,再从左到右遍历一次链表,设置random指针。分离链表,返回。
RandomListNode *copyRandomList(RandomListNode *head)
{
// write your code here
RandomListNode *p = head;
RandomListNode *dest, *t = NULL;
while (p != NULL) {
t = new RandomListNode(p->label);
t->next = p->next;
t->random = p->random;
p->next = t;
p = t->next; //取得源链表中的下一个结点
}
p = head;
while (p != NULL) {
t = p->next;
if (t->random != NULL) { // 此处需要判断源节点的random是否为空,如果不为空才需要更新
t->random = t->random->next;
}
p = t->next;
}
p = head;
dest = p->next;
while (p != NULL) {
t = p->next;
p->next = t->next; //新旧链表分离的旧(源)链表
p = t->next;
if (p != NULL) {
t->next = p->next; //新旧链表分离的新链表
}
}
return dest;
}