单链表

链表

  数组是程序设计语言所提供的一种非常特殊有用的数据结构,但是,数组至少有两个局限:(1)编译器就要知道大小;(2)数组中的数据在计算机内存中是以相同距离间隔开的,这意味着要在数组中插入一个数据,需要移动该数组中的其它数据。链表就不存在这些问题,链表是节点的集合,节点中存储着数据并连接到到其它的节点。通过这种方式,节点可以在内存中的任何位置,每个节点都存储着链表中其它节点的地址,因此数据很容易从一个节点到达另一个节点。链表的实现方式有很多种,但最灵活的实现方法是使用指针。

单链表

  如果一个节点将指向另一个节点的指针作为数据成员,那么多个这样的节点可以连接起来,只用一个变量就能够访问整个节点序列。这样的节点序列就是最常用的链表的实现方法。链表是一种由节点组成的数据结构,每个节点都包含某些信息以及指向链表中另一个节点的指针。如果序列中的节点只包含指向后继节点的链接,该链表就成为单向链表。有时我们在单链表的第一个结点之前附设一个节点,称之为头节点。头节点的数据域可以不存储任何信息,也可以存储如线性表的长度等类的附加信息,头节点的指针域存储指向第一个节点的指针。

(a)非空表;(b)空表
(a)非空表;(b)空表

单循环链表

  循环链表时另一种形式的链式存储结构。它的特点时表中最后一个节点的指针指向头节点,整个链表形成一个环。由此,从表中任一个节点出发均可找到其它节点。如图:
(a)非空表;(b)空表
在这里插入图片描述
  循环链表的操作和线性表基本一致,差别仅在于算法中的循环条件不是p或者p->next是否为空,而是它们是否等于头指针。但有的时候,若再循环链表中设立尾指针而不设头指针,如图:
在这里插入图片描述
这样可使某些操作简化。例如将两个线性(比如上图)表合并成一个表时,仅需要将一个表的尾指针和另一个表的表头相接。如图,这个操作仅需要改变两个指针即可,运行时间为O(1)。如下图:
在这里插入图片描述

源码实现

几个主要的功能:

  1. 初始化:创建带有头节点的链表
  2. 尾插法建立单链表
  3. 头插法建立单链表
  4. 求长度:求线性表中的节点的个数
  5. 取节点:取给定位置的节点
  6. 查节点:查找给定数据的节点的位置
  7. 插入节点:在指定的节点之后插入节点
  8. 插入节点:在指定的节点之前插入节点
  9. 删除节点:删除指定节点的后继节点
  10. 删除节点:删除第i个节点
  11. 遍历节点:从头到尾扫描线性表
  12. 单链表的反转

源码:

#include <stdio.h>
#include <stdlib.h>
// 元素类型
typedef int ElemType;
// 定义节点
typedef struct node{
    ElemType data;
    struct node *pNext;
}LinkListNode;

/*
 * 初始化: 创建带有头节点的链表
 * 返回值: 返回创建好的头节点
 */
LinkListNode *initLinkList()
{
    LinkListNode *pHead = NULL;
    pHead = (LinkListNode *)malloc(sizeof(LinkListNode));
    if (pHead != NULL)
        pHead->pNext = NULL;
    return pHead;
}

/*
* 尾插法建立单链表
* 参数: arr:传入的顺序表,length:顺序表的长度
* 返回值: 返回链表的头节点的指针
*/
LinkListNode *createTailLinkList(ElemType arr[], int length)
{
    if (length <= 0)
        return NULL;

    LinkListNode *pHead, *p, *q;
    pHead = (LinkListNode *)malloc(sizeof(LinkListNode));
    q = pHead;
    for (int i = 0; i < length; ++i) {
        p = (LinkListNode *)malloc(sizeof(LinkListNode));
        p->data = arr[i];
        q->pNext = p;
        q = p;
    }
    p->pNext = NULL;
    return pHead;
}

/*
* 头插法建立单链表
* 参数: arr:传入的顺序表,length:顺序表的长度
* 返回值: 返回链表的头节点的指针
*/
LinkListNode *createHeadLinkList(ElemType arr[], int length)
{
    if (length <= 0)
        return NULL;
	//p是新加入节点,q是当前节点
	LinkListNode* pHead, *p, *q;
	int i;
	q = NULL;
	for (i = length - 1;i >= 0;i--) {
	    p = (LinkListNode*)malloc(sizeof(LinkListNode));
	    p->data = arr[i];
	    p->pNext = q;
	    q = p;
	}
	pHead = (LinkListNode*)malloc(sizeof(LinkListNode));
	pHead->pNext = q;
	return pHead;
}

/*
 * 求长度: 求线性表中的节点的个数
 * 参数: pHead,链表的头指针
 * 返回值: 链表的节点数量。错误时返回-1
 */
int getSizeOfLinkList(LinkListNode *pHead)
{
    if (pHead == NULL)
        return -1;

    int cnt = 0;
    while (pHead->pNext) {
        ++cnt;
        pHead = pHead->pNext;
    }
    return cnt;
}

/*
 * 取节点: 取给定位置的节点
 * 参数: pHead,链表的头指针;pos,节点的位置
 * 返回值: 找到,返回节点的指针,否则返回NULL
 */
LinkListNode *getLinkListNode(LinkListNode *pHead, int pos)
{
    if (pHead == NULL || pos <= 0)
        return NULL;

    int curPos = 0;
    LinkListNode *p = pHead;

    while (p->pNext != NULL) {
        p = p->pNext;    
        if (curPos == pos)
            return p;
        ++curPos;
    }
    return NULL;
}

/*
 * 查节点: 查找给定数据节点的位置
 * 参数: pHead,链表的头指针;elem,节点的元素数据
 * 返回值: 找到,返回节点的指针,否则返回NULL
 */
LinkListNode *locateLinkList(LinkListNode *pHead, ElemType objData)
{
    if (pHead == NULL)
        return NULL;
    LinkListNode *p = pHead->pNext;
    while (p->data != objData && p->pNext != NULL) {
        p = p->pNext;
    }
    if (p->data == objData)
        return p;
    else
        return NULL;
}

/*
 * 插入节点:在指定的节点之后插入节点
 * 参数: ptr,要插入的节点的位置的节点;elem,节点的元素数据
 */
void insertAfterNode(LinkListNode *ptr, ElemType elem)
{
    if (ptr == NULL)
        return;
    LinkListNode *p = (LinkListNode *)malloc(sizeof(LinkListNode));
    p->pNext = ptr->pNext;
    p->data = elem;
    ptr->pNext = p;
}
/*
 * 插入节点:在指定的节点之前插入节点
 * 参数: pHead,链表的头指针;ptr,要插入的节点的位置的节点;elem,节点的元素数据
 */
void insertBeforeNode(LinkListNode* pHead, LinkListNode *ptr, ElemType elem)
{
    if (ptr == NULL || pHead == NULL)
        return;
    LinkListNode *p = (LinkListNode *)malloc(sizeof(LinkListNode));
    p->pNext = ptr;
    p->data = elem;
    LinkListNode *q = pHead;
    while (q->pNext != ptr) {
        q = q->pNext;
    }
    q->pNext = p;
}

/*
 * 删除节点:删除指定节点的后继节点
 * 参数: ptr,删除ptr节点之后的节点
 * 返回值: 返回删除的节点
 * 注意: 删除返回目标节点的地址,并不涉及到动态空间的回收
 *      在动态回收空间的要求中,应该遵循的原理是谁污染谁治理
 *      在顺序表中的删除就是逻辑上的删除,就是说我们的这个节点不再
 *      存在于当前的顺序表中了
 */
LinkListNode *deleteAfterLkList(LinkListNode *ptr)
{
    LinkListNode *fptr;
    fptr = ptr->pNext;
    ptr->pNext = fptr->pNext;
    return fptr;
}

/*
 * 删除节点:删除第i个节点
 * 参数: pHead,链表的头指针;i,第i个节点
 * 返回值: 删除成功返回删除的节点指针,否则返回NULL
 */
LinkListNode* Delete_i_LkList(LinkListNode* pHead, int i)
{
    if (i < 0 || pHead == NULL)
        return NULL;

    LinkListNode*ptr, *qPtr = NULL;
    ptr = getLinkListNode(pHead, i - 1);//找到i的前继节点
    if (ptr != NULL && ptr->pNext != NULL)
        qPtr = deleteAfterLkList(ptr);
    return qPtr;
}

/*
 * 遍历节点
 * 参数: pHead,链表的头指针
 */
void showLkList(LinkListNode* pHead)
{
    if (pHead == NULL)
        return;
    LinkListNode* p = pHead->pNext;
    while (p != NULL) {
        printf(" %d", p->data);
        p = p->pNext;
    }
}

/*
 * 单链表的反转
 * 参数: pHead,传入链表的头指针
 * 大体思路:首先定义三个LinkListNode指针变量Front指向头指针,Cur指向第一个节点,Next指向第一个节点的下一个节点,
 *          然后Cur的成员变量pNext指向Front,接着Front指向Cur,Cur再指向Next,依次类推挨个节点指向前一个节点。
 */
LinkListNode *LinkListReverse(LinkListNode *pHead)
{
    if (pHead == NULL || pHead == NULL)
        return NULL;

    if (pHead->pNext->pNext == NULL)
        return pHead;

    LinkListNode *Front = NULL, *Cur = NULL, *Next = NULL;
    Front = pHead;
    Cur = Front->pNext;
    Next = Cur->pNext;
    Cur->pNext = Front;
    Front = Cur;
    Cur = Next;
    while (Cur->pNext != NULL) {
        Next = Cur->pNext;
        Cur->pNext = Front;
        Front = Cur;
        Cur = Next;
    }
    // 当进行到最后一个节点的时候Cur->pNext == NULL,所以跳出循环,
    // 但是此时Cur->pNext还是NULL,需要Next = Cur->pNext;
    Cur->pNext = Front;
    // pHead一直未进行操作,所以此时的pHead->pNext指向的是第一个节点
    // 反转完成之后第一个节点变成了最后一个节点,所以取值为NULL
    pHead->pNext->pNext = NULL;

    pHead->pNext = Cur;
    return pHead;
}



int main()
{
    ElemType MySeq[] = { 1,2,3,4,5 };
    LinkListNode* pHead1 = createHeadLinkList(MySeq, 5);
    insertBeforeNode(pHead1, locateLinkList(pHead1, 2), 111);
    LinkListNode* pHead2 = createTailLinkList(MySeq, 5);
    getchar();
    return 0;
}

方法不太全,大家可以根据自己的需求进行扩展。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值