逆置单链表c语言不带头节点,单链表-不带头结点

链表,别名链式存储结构或单链表,用于存储逻辑关系为 "一对一" 的数据。与顺序表不同,链表不限制数据的物理存储状态,换句话说,使用链表存储的数据元素,其物理存储位置是随机的。

链表中数据元素的构成

每个元素本身由两部分组成:

本身的信息,称为“数据域”;

指向直接后继的指针,称为“指针域”。

这两部分信息组成数据元素的存储结构,称之为“结点”。n个结点通过指针域相互链接,组成一个链表。

typedef int ElemType;

typedef struct LinkNode

{

ElemType data;

struct LinkNode *next;

}Node,*pNode;

//为新节点开辟空间 这个一块存储空间 包括一个数据域+一个指针域

pNode NewSpace(ElemType data)

{

pNode tmp = (Node *)malloc(sizeof(Node));

tmp->data = data;

tmp->next = NULL;

return tmp;

}

头结点、头指针和首元结点

头结点:有时,在链表的第一个结点之前会额外增设一个结点,结点的数据域一般不存放数据(有些情况下也可以存放链表的长度等信息),此结点被称为头结点。若头结点的指针域为空(NULL),表明链表是空表。头结点对于链表来说,不是必须的,在处理某些问题时,给链表添加头结点会使问题变得简单。

首元结点:链表中第一个元素所在的结点,它是头结点后边的第一个结点。

头指针:永远指向链表中第一个结点的位置(如果链表有头结点,头指针指向头结点;否则,头指针指向首元结点)。

头结点和头指针的区别:头指针是一个指针,头指针指向链表的头结点或者首元结点;头结点是一个实际存在的结点,它包含有数据域和指针域。两者在程序中的直接体现就是:头指针只声明而没有分配存储空间,头结点进行了声明并分配了一个结点的实际物理内存。

单链表初始化

//初始化

void InitNodeList(pNode *pHead) {

*pHead = NULL;

}

尾插

//尾插

void InsertNodeBack(pNode *pHead, ElemType e) {

assert(NULL != pHead);

pNode temp = *pHead; //创建临时结点temp

//判断节点是否为空,如果为空就给让分配空间

if (*pHead == NULL) {

*pHead = NewSpace(e);

}

else {

while (temp->next != NULL) {

temp = temp->next;

}

temp->next = NewSpace(e);

}

}

首插

//头插

void InsertNodeFront(pNode *pHead, ElemType e) {

assert(NULL != pHead);

pNode temp; //创建临时结点temp

temp = NewSpace(e);

temp->next = *pHead;

*pHead = temp;

}

尾删

//尾删

void RemoveNodeBack(pNode *pHead) {

assert(NULL != pHead);

pNode temp = *pHead;

if (temp == NULL) {

printf("Seqlist is Empty...\n");

return;

}

else if (temp->next == NULL) {

free(temp);

*pHead = NULL;

}

else {

temp = *pHead;

pNode pre = NULL;

while (temp->next != NULL) {

pre = temp;

temp = temp->next;

}

pre->next = NULL;

free(temp);

temp->next = NULL;

}

}

首删

//首删

void RemoveNodeFront(pNode *pHead) {

assert(pHead);

pNode temp = *pHead;

if (temp == NULL) {

printf("Seqlist is Enpty...\n");

return;

}

else if(temp->next == NULL) {

*pHead = NULL;

free(temp);

}

else {

*pHead = temp->next;

free(temp);

}

}

删除指定位置节点

//指定位置删除

void RemoveNodePos(pNode *pHead, int pos) {

assert(pHead);

pNode pre = *pHead;

if (pos == 1) {

*pHead = pre->next;

free(pre);

}

else {

for (int i=1; i

pre = pre->next;

if(pre == NULL) {

printf("找不到要删除的坐标,超过了最大长度...\n");

return;

}

}

pNode cur = pre->next;

pre->next = cur->next;

free(cur);

}

}

在指定位置插入节点

//指定位置插入

void InsertNodePos(pNode *pHead, ElemType data, int pos) {

assert(pHead);

pNode pre = *pHead;

if (pos == 1) {

pNode temp = NewSpace(data);

*pHead = temp;

temp->next = pre;

*pHead = temp;

}

else {

for (int i=1; i

pre = pre->next;

if (pre == NULL) {

printf("找不到,超足了链表最大长度...\n");

return;

}

}

pNode cur = NewSpace(data);

cur->next = pre->next;

pre->next = cur;

}

}

删除链表中所有值为data的节点

//删除所有data的值

void RemoveNodeDataAll(pNode *pHead, ElemType data) {

assert(pHead);

pNode pre = *pHead;

//只有一个元素并且值为要找的元素

if (pre->data == data && pre->next == NULL) {

free(pre);

*pHead = NULL;

return;

}

pNode cur = *pHead;

while (1) {

if (pre && pre->data == data && pre->next != NULL && pre->next->data != data) {

*pHead = pre->next;

free(pre);

pre = *pHead;

}

while (pre && pre->data != data) {

cur = pre;

pre = pre->next;

}

if (pre && pre->data == data && pre->next != NULL) {

cur->next = pre->next;

free(pre);

pre = cur->next;

}

else if(pre && pre->data == data && pre->next != NULL) {

cur->next = NULL;

free(pre);

pre = cur;

}

else {

return;

}

}

}

获取节点长度

//计算链表长度

int NodeLength(pNode pHead) {

assert(pHead);

int count = 0;

pNode pre = pHead;

while (pre) {

count ++;

pre = pre->next;

}

return count;

}

打印

//打印链表

void PrintNodeList(pNode pHead) {

assert(pHead);

pNode temp = pHead; //创建临时结点temp

while (temp != NULL) {

printf("%d\t", temp->data);

temp = temp->next;

}

printf("\n");

}

逆置1

//逆置,从头到尾访问旧的链表,每访问一个,

//就将节点的数据采取头插的方式存入新的链表,释放原来链表的空间

void ReverseNode1(pNode *pHead) {

assert(pHead);

pNode pre = *pHead;

pNode new = NULL;

while (pre) {

pNode cur = pre;

pNode temp = NewSpace(pre->data);

temp->next = new;

pre = pre->next;

new = temp;

free(cur);

}

*pHead = new;

}

逆置2

//逆置,从头到尾访问旧的链表,每访问一个就把这个节点插到新的链表中

void ReverseNode2(pNode *phead) {

assert(phead);

pNode pre = *phead;

pNode new = NULL;

while (pre) {

pNode cur = pre;

pre = pre->next;

cur->next = new;

new = cur;

}

*phead = new;

}

逆置3

//链表逆置

void ReverseNode3(pNode *phead) {

assert(phead);

pNode temp = *phead;

pNode temp_pre = NULL;

pNode temp_next = NULL;

if (temp->next == NULL) {

return;

}

else if(temp->next->next == NULL) {

return;

}

while (temp) {

temp_next = temp->next;// 保存当前节点的下一个节点

temp->next = temp_pre;//更新当前节点的指针域

temp_pre = temp;//更新当前节点上一个节点的位置

temp = temp_next;//更新当前节点的位置

}

*phead = temp_pre;

}

顺序表拼接1

//拼接C=A+B

void JointNodeNew(pNode *La, pNode *Lb, pNode *Lc) {

pNode pa = *La;

pNode pb = *Lb;

pNode pc = *Lc;

if (pa && pb == NULL) {

*Lc = pa;

return;

}

if (pb && pa == NULL) {

*Lc = pb;

return;

}

if (pb->data <= pa->data) {

pNode temp = pb;

pb = pb->next;

*Lc = temp;

pc = *Lc;

}

else if (pb->data > pa->data) {

pNode temp = pa;

pa = pa->next;

*Lc = temp;

pc = *Lc;

}

while (pa && pb) {

if (pa->data <= pb->data) {

pc->next = pa;

pc= pa;

pa = pa->next;

}

else {

pc->next = pb;

pc= pb;

pb = pb->next;

}

}

pc->next = pa?pa:pb;

}

顺序表拼接2

//链接拼接 A = A+B temp->next = pa

//pa->next = NULL 说明temp是最后一个元素, 只需要把Lb拼接到La

//temp->next = pb;

//使用一个tmp指向Lb的头节tmp=pb;

//当要把pb的节点拼接到La,首先把头节点只想pb的下一个节点tmp=pb->next

//把pb插入到La中之后,再把pb指向Lb的头节点tmp

void JointNode(pNode *La, pNode *Lb) {

assert(La);

assert(Lb);

pNode pa = *La;

pNode pb = *Lb;

pNode temp = pa;

pNode tmp = pa;

if (pa && pb == NULL) {

*La = pa;

}

else if (pb && pa == NULL) {

*La = pb;

}

if(pb->data < pa->data) {

tmp = pb->next;

*La = pb;

temp = pb;

pb->next = pa;

pb = tmp;

}

while (pa && pb) {

if (pa->data > pb->data) {

tmp = pb->next;

temp->next = pb;

pb->next = pa;

temp = pb;

pb = tmp;

}

else {

temp = pa;

pa = pa->next;

}

}

if (pb == NULL) {

return;

}

if (pb != NULL) {

pa->next = pb;

}

}

约瑟夫环

//约瑟夫环(约瑟夫问题)是⼀个数学的应⽤问题:

// 已知n个⼈(以编号1, 2, 3...n分别表⽰)围坐在⼀张//圆桌周围。

// 从编号为k的⼈开始报数,数到m的那个⼈出列;他的下⼀个⼈又从k开始报数,数到m的那个⼈

// 又出列;依此规律重复下去,直到圆桌周围的⼈全部出列

//约瑟夫环

void NodeJosephCycle(pNode *pHead, int k) {

assert(pHead);

pNode cur = *pHead;

//第一步,先让链表构成环,即需要一个变量(tail)遍历至最后一个节点,

//然后让最后一个结点的next指向*phead

pNode tail = *pHead;

while (tail->next != NULL) {

tail = tail->next;

}

tail->next = *pHead;

//第二步,让链表从起始地址进行查询,找到k的位置,然后删掉这个结点,

//从该结点的下一个结点继续找K个位置,再次删掉,直到剩一个结点为止

//cur是我们要删除的结点,用pre来标识cur的上一个位置。

while (cur->next != cur) {

pNode pre = NULL;

int i=0;

for (; i

pre = cur;

cur = cur->next;

}

pre->next = cur->next;

printf("delete....%d\n", cur->data);

free(cur);

cur = pre->next;

}

printf("save...%d\n", cur->data);

cur->next = NULL;

}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值