链表练习题

从尾到头输出单链表

void ReversePrint(SListNode *phead)               //phead为链表头指针
{
    SListNode *head = phead;
    SListNode *tail = head;
    if (phead == NULL)                            //链表为空
    {
        printf("the list was empty!");
        return;
    }
    if (head->next == NULL)                       //链表内仅有一个节点
    {
        printf("%d->NULL", head->data);
    }
    else//链表内至少2节点
    {
        ReversePrint(head->next);
        printf("%d->", head->data);
    }

}

删除一个无头单链表的非尾节点

                                          //删除一个无头单链表的非尾节点
void DelListNode(SListNode **pos)         //pos是指向链表头指针的二级指针
{
    assert(pos);
    SListNode *next = (*pos)->next;
    (*pos)->data = next->data;
    (*pos)->next = next->next;
    free(next);
}

在无头单链表的一个节点前插入一个节点(不能遍历链表)

void PlistInsert(SListNode **pos, DataType x)
{
    assert(pos);
    if ((*pos)->next == NULL)
    {
        SListNode *newnode = BuySListNode((*pos)->data);
        (*pos)->next = newnode;
        (*pos)->data = x;
    }
    else
    {
        SListNode *next = (*pos)->next;
        SListNode *newnode = BuySListNode((*pos)->data);
        newnode->next = next;
        (*pos)->next = newnode;
        (*pos)->data = x;
    }
}

单链表实现约瑟夫环(JosephCircle)

void Joseph(SListNode **fflag,int x)       //链表已构成环
{
    SListNode *flag = (*fflag);
    SListNode *next = (*fflag);

    if (flag->next ==flag)                 //链表内部只有一个节点
    {
        printf("%d", flag->data);
        return;
    }
    while (flag->next != flag)             //链表内至少2个节点
    {
        SListNode *del =NULL;
        int num = x;
        for (int i = 1;i < x-1;i++)
        {
            flag = flag->next;
        }
        del = flag->next;
        next = flag->next->next;
        printf("%d ", del->data);
        flag->next = next;
        free(del);
        flag = next;

    }
    printf("%d", flag->data);

}

逆置/反转单链表

SListNode *reverse_list(SListNode **pphead)
{
    assert(pphead);
    if ((*pphead)->next == NULL)            //链表只有一个节点
    {
        return *pphead;
    }
    //链表至少有2个节点
    SListNode *p1 = *pphead;
    SListNode *p2 = (*pphead)->next;
    SListNode *p3 = (*pphead)->next->next;
    while (p3)
    {
        p2->next = p1;
        p1 = p2;
        p2 = p3;
        p3 = p3->next;
    }
    p2->next = p1;
    (*pphead)->next = NULL;
    return p2;

}

单链表排序(冒泡排序&快速排序)

void bubble_sort(SListNode **pphead)  //冒泡排序
{
    assert(pphead);
    if ((*pphead)->next == NULL)      //链表只有一个节点
    {
        return;
    }
    SListNode *phead = *pphead;
    int i = 0;
    while (phead->next)              //计算链表结点数
    {
        phead = phead->next;
        i++;
    }
    for (;i > 0;i--)
    {
        int flag = 1;                //假设链表已经有序
        for (phead = *pphead;phead->next != NULL;phead = phead->next)
        {

            if (phead->data > (phead->next)->data)
            {
                int tmp = phead->data;
                phead->data = (phead->next)->data;
                (phead->next)->data = tmp;
                flag = 0;
            }
        }
        if (flag == 1)//判断是否交换过
        {
            return;
        }
    }
}

合并两个有序链表,合并后要求链表仍然有序

SListNode *merge_list(SListNode *phead, SListNode *phead1)
{
    SListNode *newhead = NULL;         //定义两个指针,一个为新链表的头,一个为新链表的尾
    SListNode *tail = NULL;
    if (phead == NULL)                 //判断是否有一个链表为空
    {
        return phead1;
    }
    if (phead1 == NULL)
    {
        return phead;
    }
    if (phead->data < phead1->data)    //先给新链表一个头节点
    {
        newhead = phead;
        phead = phead->next;
    }
    else
    {
        newhead = phead1;
        phead1 = phead1->next;
    }
    tail = newhead;
    while (phead&&phead1)              //当两个链表都不为空时,对比元素,取小的链在tail后面
    {
        if (phead->data < phead1->data)
        {
            tail->next = phead;
            phead = phead->next;
        }
        else
        {
            tail->next = phead1;
            phead1 = phead1->next;
        }
        tail = tail->next;
    }
    if (phead)                        //如果某一个链表没有链完就补在tail后面
    {
        tail->next = phead;
    }
    if (phead1)
    {
        tail->next = phead1;
    }
    return newhead;
}

查找链表的中间节点,要求只能遍历一次链表

SListNode *search_mid_node(SListNode *pphead)
{
    if (pphead == NULL||(pphead)->next==NULL)       //当链表为空或者只有一个节点
    {
        return pphead;
    }
                                                    //链表至少有2个节点
    SListNode *slow = pphead;
    SListNode *fast = pphead;
    while (fast&&fast->next)
    {
        slow = slow->next;
        fast = fast->next->next;

    }
    return slow;

}

查找单链表的倒数第k个节点,要求只能遍历一次链表

SListNode *search_last_k(SListNode *phead, int k)
{
    assert(phead);
    SListNode *tail = phead;
    while (--k)                          //确保链表中至少有k个节点
    {
        if (tail->next == NULL)
        {
            printf("error\n");
            return NULL;
        }

        tail = tail->next;
    }
    while (tail->next)                   //头尾指针同时向后移动
    {
        tail = tail->next;
        phead = phead->next;
    }
    return phead;
}

删除单链表的倒数第k个节点。

SListNode *delete_last_k(SListNode **pphead,int k)
{
    SListNode *phead = *pphead;
    SListNode *p = phead;
    SListNode *pos= search_last_k(phead, k);  //利用search_last_k找到倒数第k个节点    
    SListNode *next = NULL;                     
    assert(pos);                              //确保pos不为空
                                              //考虑pos是否是首节点(头删)或尾节点(尾删)
    if (pos == phead)
    {
        phead = phead->next;
        free(pos);
        return phead;
    }
    else                                      //一般情况,包含尾删
    {
        while (p->next != pos)
        {
            p = p->next;                     //找到pos前一个节点
        }
        next = pos->next;                    //pos后面的节点(可以是空)
        p->next = next;
        free(pos);
        return phead;
    }

}

判断单链表是否带环?若带环,求环的长度?求环的入口点?并计算每个算法的时间复杂度与空间复杂度

SListNode *check_circle(SListNode *phead)   //是否带环?
{
    assert(phead);
    SListNode *fast = phead;          
    SListNode *slow = phead;
    while (fast)
    {                         //定义快慢指针,快的每次走2个节点,慢的走1个节点
        slow = slow->next;
        fast = fast->next;
        if (fast)
        {
            fast = fast->next;
        }
        if (fast == slow)     //相等说明快慢指针在环内相遇
        {
            printf("该链表带环\n");
            return slow;
        }
    }
    printf("该链表不带环\n");  //说明该链表有尾
    return NULL;


}
int circle_lenth(SListNode *meet) //计算环的长度  参数是环内快慢指针的相遇点
{
    SListNode *flag = meet;
    int i = 0;
    do
    {
        flag = flag->next;
        i++;
    } 
    while (flag != meet);
    return i;
}

计算环的入口点相对较难,代码比较简单,先要理清思路(画的很挫…将就一下)
这里写图片描述

SListNode *search_entrance(SListNode *phead, SListNode *meet)
{
    while (phead != meet)
    {
        phead = phead->next;
        meet = meet->next;
    }
    return meet;
}

判断两个链表是否相交,若相交,求交点(假设链表不带环)

这个问题要与下面的带环问题放在一起讨论,如图:

这里写图片描述

不带环的只有1,2两种情况,交与不交,我们可以知道如果相交,那么这两个链表的最后一个节点的地址一定相等(不然就交不上了)。这就可以作为我们判断不带环类型是否相交的依据。

而如果确认相交,分别算出两个链表的长度,求出差值x。让长的那个先走两个链表长度的差值的长度。再让两个链表同时走,相等即是交点。

int check_cross(SListNode *phead1, SListNode *phead2)
{
    if (phead1 == NULL || phead2 == NULL)         //某链表为空一定不相交
    {
        printf("不相交\n");
        return 0;
    }
    while (phead1)                                //求2链表最后位置
    {
        phead1 = phead1->next;
    }
    while (phead2)
    {
        phead2 = phead2->next;
    }
    if (phead1 == phead2)                         //若相交,最后节点地址一定相等,反之则不等
    {
        printf("相交\n");
        return 1;
    }
    else
    {
        printf("不相交\n");
        return 0;
    }
}
SListNode *find_focus_point(SListNode *phead1, SListNode *phead2)//在已经确认相交的基础上
{
    int i = 0;
    int j = 0;
    SListNode *tail1 = phead1;
    SListNode *tail2 = phead2;
    while (tail1)
    {
        tail1 = tail1->next;
    }
    while (tail2)
    {
        tail2 =tail2->next;
    }
    int num = abs(tail1 - tail2);
    if (tail1 > tail2)
    {
        while (num--)
        {
            phead1 = phead1->next;
        }
    }
    else
    {
        while (num--)
        {
            phead2 = phead2->next;
        }
    }
    while (phead1 != phead2)
    {
        phead1 = phead1->next;
        phead2 = phead2->next;
    }
    return phead1;

判断两个链表是否相交,若相交,求交点(假设链表带环)

这里写图片描述

还是这个图,如果带环,分有1环和两环2种情况。如果只有1环(即图3);如果是2环(图456),有不交(4)和相交(56)两种情况。现在依次对这几种情况分析:

图3(即一环一不带环)一定是不相交的,如果相交一定变为2环

判断图4可以让meet1不动,meet2继续遍历,如果再次到meet2前仍没有与meet1相遇,则说明两个相遇点不在一环内,即图4,反之则为图5,图6.

图5和图6的区别是一个是环外相交,一个是环内相交。辨别思路如下:

分别让phead1和meet1,phead2和meet2对应,用求环入口点的方法(上面有提及)分别求出入口点,如果两个入口点地址相等,这说明是环外相交(图5)。反之则是环内相交(图6)。

——————————————————————————————————————————————

复杂链表的复制。一个链表的每个节点,有一个指向next指针指向 下一个节点,还有一个random指针指向这个链表中的一个随机节点 或者NULL,现在要求实现复制这个链表,返回复制后的新链表。

思路:

第一步:仍然是根据原始链表的每个结点N 创建对应的 N’。把 N’链接在N的后面。

第二步:设置复制出来的结点的 sibling。假设原始链表上的 N 的 sibling 指向结点 S,那么其对应复制出来的 N’是 N的 pext 指向的结点,同样 S’也是 S 的 next 指向的结点。

把这个长链表拆分成两个链表。把奇数位置的结点用 next 链接起来就是原始链表,把偶数位置的结点用 next 链接起来就是复制 出来的链表。

typedef struct Node
{
    int value ;
    struct Node *next ;
    struct Node *random ;
}Node;
Node *deepCopy (Node *head)
 {
    Node *p = head, *q = head->next;     
    //step 1
    {    
        Node *newNode = (Node *)malloc(sizeof(Node));
        newNode->next = p->next;
        p->next = newNode;
        newNode->value = p->value;
        newNode->random = NULL;
        p = q;
        q = q->next;
    }

    //step 2
    p = head;
    q = p->next;
    while (q != NULL)
     {
        if (p->random != NULL)
            q->random = q->random->next;
        if (q->next == NULL)
            break;
        p = q->next;
        q = p->next;
    }  
//step 3
    newNode = head->next;
    p = head; q = p->next;
    while (q != NULL) 
    {
        p->next = q->next;
        if (q->next == NULL)
            break;
        q->next = p->next->next;

        p = p->next;
        q = q->next;
    }
    return newNode;
}

———————————————————————————————————————————————

求两个已排序单链表中相同的数据。

思路:

比较两个指针所指数据的大小,小的向后移,大的不动。遇到相等的,开辟一个新节点存储数据。直到一链表到尾,将开辟的节点链成链表即可。

SListNode *check_common_node(SListNode *phead, SListNode *phead1)
{
    SListNode *newhead = NULL;
    SListNode *tail = newhead;
    if (phead == NULL || phead1 == NULL)//任意一个链表为空返回空
    {
        return NULL;
    }
    while (phead&&phead1)
    {
        if (phead->data < phead1->data)       //小的向后走
        {
            phead = phead->next;
        }
        if (phead->data > phead1->data)
        {
            phead1 = phead1->next;
        }
        else                                  //相等时开辟节点,链在tail后
        {
            SListNode *newnode = BuySListNode(phead->data);

            if (newhead == NULL)
            {
                newhead = newnode;
                tail = newnode;
            }
            else
            {
                tail->next = newnode;
                tail = newnode;
            }
            phead = phead->next;
            phead1 = phead1->next;
        }
    }
    return newhead;
}

猜你喜欢

转载自blog.csdn.net/ferlan/article/details/79647978