线性表(顺序存储/链式存储)

数据结构中按逻辑结构分为4类:集合、线性表、树、图。按存储结构分为2类:顺序存储、链式存储。

一些辅助定义:

#define MAXSIZE 20

#define OK 0
#define ERROR 1

typedef int Status;
typedef int ElemType;

线性表结构定义:
//顺序存储
typedef struct
{
    ElemType data[MAXSIZE];
    int length;
}SqList;
//链式存储节点
typedef struct Node
{
    ElemType data;
    struct Node *next;
}Node,*PNode;
//静态链式线性表
typedef struct {
    ElemType data;
    int cur;
}Component,StaticLinkList[MAXSIZE];


所有代码都是可运行的,为方便测试,现附上main()函数。

int main(int argc, const char * argv[])
{
    PNode pNode = NULL;
    PNode _pNode = NULL;
    Status ret = ERROR;
    ret = CreatLinkList(&pNode, MAXSIZE);
    if (ret) {
        printf("创建链表失败!\n");
        return ERROR;
    }
    ret = CreatLinkList(&_pNode, MAXSIZE);
    if (ret) {
        printf("创建链表失败!\n");
        return ERROR;
    }
//    PrintLinkList(pNode);
//    ReverseLinkList(&pNode);
//    PrintLinkList(_pNode);
//   UnionLinkList(&pNode, _pNode);
//    PrintLinkList(pNode);
    PNode entryNode = NULL;
    PNode *mtpNode = (PNode *)malloc(sizeof(Node));
    ret = IsCircleLinkList(pNode, mtpNode);
    if (!ret) {
        printf("该链表无环!\n");
    }else{
        entryNode = FindEntryNode(pNode, (*mtpNode));
        printf("该链表有环!\n环节点的值为:%d\n",entryNode->data);
    }
    return 0;
}


1.线性表的顺序存储

元素插入

Status ListInsert(SqList *L, int i, ElemType e)
{
    i -= 1;
    
    if (L == NULL || L->length<i || i < 0 || L->length >= MAXSIZE) {
        printf("链表为空,或者插入位置非法!\n");
        return ERROR;
    }
    
    for (int j = L->length-1; j >= i; j--) {
        L->data[j+1] = L->data[j];
    }
    
    L->data[i] = e;
    L->length += 1;
    
    return OK;
}


元素删除
Status ListDelete(SqList *L, int i, ElemType *e)
{
    if (L == NULL || i < 1 || i > L->length) {
        printf("链表为空,或者删除位置溢出!");
        return ERROR;
    }
    
    *e = L->data[i -1];
    
    for (int j = i; j < L->length; j++) {
        L->data[j-1] = L->data[j];
    }
    L->length -= 1;
    
    return OK;
}

2.线性表的链式存储(有两种实现方式(动态链表/静态链表),其中静态链表用数组实现,数组下标相当于动态链表中的指针域)

//动态链表

创建链表

Status CreatLinkList(PNode *ppNode, int n)
{
    if (n < 1) {
        printf("参数错误!\n");
        return ERROR;
    }
    *ppNode = (PNode)malloc(sizeof(Node));//头结点
    if (!(*ppNode)) {
        printf("内存分配失败!\n");
        return ERROR;
    }
    (*ppNode)->data = 0;
    (*ppNode)->next = NULL;
    
    int i = 0;
    PNode _ppNode = NULL;
    PNode head_pNode = (*ppNode);
    for (i = 0; i < n; i++) {
        _ppNode = (PNode)malloc(sizeof(Node));
        if (_ppNode) {
            _ppNode->data = i;
            _ppNode->next = NULL;
            head_pNode->next = _ppNode;
            head_pNode = head_pNode->next;
        }else{
            printf("内存分配失败!\n");
            return ERROR;
        }
    }
    (*ppNode)->data = i;
    
    return OK;
}


打印链表
void PrintLinkList(PNode pNode)
{
    PNode _ppNode = pNode->next;
    if (!pNode || !_ppNode) {
        printf("链表为空!\n");
        return;
    }
    printf("打印链表:\n");
    while (_ppNode) {
        printf("%d\t",_ppNode->data);
        _ppNode = _ppNode->next;
    }
    printf("\n打印结束\n");
}

链表逆序(这个应该用前插法,我这写法太笨了,大家就不要看了。)

void ReverseLinkList(PNode *ppNode)
{
    if (!ppNode || !(*ppNode)) {
        printf("链表为空!\n");
        return;
    }
    PNode left_pNode = NULL;//反转链表用
    PNode right_pNode = NULL;
    PNode _ppNode = (*ppNode)->next;
    
    while (_ppNode) {
        right_pNode = _ppNode->next;
        _ppNode->next = left_pNode;
        left_pNode = _ppNode;
        _ppNode = right_pNode;
    }
    (*ppNode)->next = left_pNode;
}


两个有序链表合并成一个有序链表
void UnionLinkList(PNode *ppNode, PNode pNode)
{
    if (!ppNode || !(*ppNode) || !pNode) {
        printf("链表非法!\n");
        return;
    }
    PNode tmp_pNode = pNode->next;//保存从pNode中取出的节点
    PNode pri_pNode = (*ppNode);//保存当前遍历节点的前一个节点
    PNode cur_pNode = pri_pNode->next;//保存当前节点
    pNode = pNode->next;
    while (tmp_pNode && cur_pNode)
    {
        while (cur_pNode)
        {
            if (tmp_pNode->data <= cur_pNode->data)
            {
                pNode = pNode->next;
                tmp_pNode->next = cur_pNode;
                pri_pNode->next = tmp_pNode;
                
                tmp_pNode = pNode;
                break;
            }
            else
            {
                pri_pNode = cur_pNode;
                cur_pNode = cur_pNode->next;
            }
        }
    }
    if (tmp_pNode && !cur_pNode) {
        pri_pNode->next = tmp_pNode;
    }

}

判断链表是否存在环

原理:通过快慢指针实现。设慢指针slow_pNode(一次走一步),快指针fast_pNode(一次走两步)。 假设当慢指针刚进入换的时候,快指针已近在环中走过了m步。则,当慢指针在环中走过 i 步时,快指针走过了m+2 * i步,此时快指针比慢指针多走了step = m + 2*i - i = m + i步。一定存在一个 i 值,使得step = n(环的长度)。代码如下:

Status IsCircleLinkList(PNode pNode, PNode *mtpNode)
{
    if (!pNode){
        printf("链表为空!\n");
        return ERROR;
    }
    PNode slow_pNode = pNode;
    PNode fast_pNode = pNode;
    
    while (fast_pNode)
    {
        if (!fast_pNode->next) {
            return OK;
        }
        fast_pNode = fast_pNode->next->next;
        slow_pNode = slow_pNode->next;
        if (fast_pNode == slow_pNode)//有环
        {
            (*mtpNode) = fast_pNode;//把相遇节点传出来,便于寻找入口节点。
            return ERROR;
        }
        else if (!fast_pNode)
        {
            return OK;
        }
    }
    return OK;
}

寻找入口节点:

原理:以相遇节点为标识,在逻辑上将有环链表分成两个单链表pNode 和 _pNode。然后计算两个链表长度的差step,让较长链表先走step步,之后两个链表挨个节点进行比较,直至找到相同节点,即为入口节点(该原理类似从两个不同的单链表中找出其相同的节点)。

图例如下:(最后的节点9不是指向NULL,而是指向前面的节点5)

1-->2-->3-->4--5-->6-->7-->8-->9

             ^                           |

                           |____________|

此时,假设相遇节点为7,以7为标示,将该链表从逻辑上分为以下两个链表:

1-->2-->3-->4--5>--6>-->7

7-->8-->9-->5-->6

实现代码:(pNode相当于上面的1节点地址,_pNode相当于7节点地址)

PNode FindEntryNode(PNode pNode, PNode _pNode)
{
    int len1 = 0, len2 = 0;
    
    PNode tmp_pNode = pNode->next;
    while (tmp_pNode != _pNode) {
        ++len1;
        tmp_pNode = tmp_pNode->next;
    }
    len1 += 1;
    
    tmp_pNode = _pNode->next;
    while (tmp_pNode != _pNode) {
        ++len2;
        tmp_pNode = tmp_pNode->next;
    }
    len2 += 1;
    
    int step = abs(len1 - len2);
    while (step) {
        pNode = pNode->next->next;
        step--;
    }
    tmp_pNode = _pNode;//由环解开的链表
    while (pNode != tmp_pNode) {//没走到虚拟的链表尾部,实际就是从相遇节点把链表分成两个假象的单链表
        if(pNode == _pNode){
            break;
        }
        pNode = pNode->next;
        _pNode = _pNode->next;
    }
    return pNode;
}

//静态链表

实现原理如图:


初始化链表

Status InitStaticLinkList(StaticLinkList space)
{
    int i;
    for (i = 0; i < MAXSIZE - 1; i++) {
        space[i].cur = i+1;
    }
    space[MAXSIZE -1].cur = 0;
    return OK;
}


分配一个节点(实际就是返回一个能用的节点位置)
int Malloc(StaticLinkList space)
{
    if (space == NULL) {
        return 0;
    }
    int i = space[0].cur;
    if (i) {
        space[0].cur = space[i].cur;
    }
    return i;
}


释放节点

void Free(StaticLinkList space, int j)
{
    space[j].cur = space[0].cur;
    space[0].cur = j;
}

返回静态链表长度
int StaticLinkListLength(StaticLinkList L)
{
    int i = L[MAXSIZE -1].cur;
    int length = 0;
    while (i) {
        ++length;
        i = L[i].cur;
    }
    return length;
}


插入
Status StaticListInsert(StaticLinkList L, int i, ElemType e)
{
    int cur = Malloc(L);
    int k = MAXSIZE -1;
    if (L == NULL || i < 1 || i > StaticLinkListLength(L)+1) {
        return ERROR;
    }
    if (cur) {
        
        L[cur].data = e;
        for (int m = 0; m < i - 1; m++) {
            k = L[k].cur;
        }
        L[cur].cur = L[k].cur;
        L[k].cur = cur;
        return OK;
    }
    return ERROR;
}


删除
Status StaticLinkListDelete(StaticLinkList L, int i)
{
    if (L == NULL || i < 1 || i > StaticLinkListLength(L)) {
        return ERROR;
    }
    int k = MAXSIZE - 1;
    for (int m = 0; m < i - 1; m++) {
        k = L[k].cur;
    }
    int j = L[k].cur;
    L[k].cur = L[j].cur;
    Free(L, j);
    return OK;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值