关于链表的一些经典问题以及相关面试题

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/weixin_40739833/article/details/79776086

关于链表的基本操作以及常见操作大家可以移步

链表及相关函数实现

当然,链表除了这些基本操作之外,还有很多很有意思以及很有深度的问题,在这里,给大家介绍一些这方面的问题

  • 逆序打印链表
  • 不允许遍历链表,在pos节点前插入新节点
  • 约瑟夫环问题求解
  • 单链表逆置
  • 单链表冒泡排序
  • 将两个有序链表合并成一个有序链表
  • 查找单链表的中间节点
  • 寻找倒数第k个节点
  • 删除倒数第k个节点
  • 判断单链表是否带环,带环返回1
  • 如果链表带环,返回环长度
  • 如果单链表带环,求出环的入口
  • 判定两个两个链表是否相交,若相交,返回交点,假设不带环

直接上代码

Linklist.h

#pragma once

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>

#define Datatype char

typedef struct LinkNode {
    Datatype data;
    struct LinkNode* next;
}LinkNode;


//创建节点
LinkNode* Creat();
//销毁节点
void Destroy(LinkNode* node);
//初始化节点
void LinklistInit(LinkNode** phead);

//逆序打印链表
void LinklistReversePrint(LinkNode* head);

//不允许遍历链表,在pos前插入节点
void LinklistInsertBefore(LinkNode* head, LinkNode* pos, Datatype value);

//约瑟夫环
LinkNode* LinklistJosephCircle(LinkNode** phead, int step);

//单链表逆置
void LinklistReverse(LinkNode** phead);
void LinklistReverse2(LinkNode** phead);

//单链表冒泡排序
void LinklistBubbleSort(LinkNode* head);

//将两个有序链表合并成一个有序链表
LinkNode* LinklistMerge(LinkNode* head1, LinkNode* head2);

//查找单链表的中间节点
LinkNode* LinklistFindMidNode(LinkNode* head);

//寻找倒数第k个节点
LinkNode* LinklistFindLastKNode(LinkNode* head, size_t k);

//删除倒数第k个节点
void LinklistRemoveLastKNode(LinkNode** phead, size_t k);

//判断单链表是否带环,带环返回1
int LinklistHasCircle(LinkNode* head);

//如果链表带环,返回环长度
size_t LinklistGetCircleLength(LinkNode* head);

//如果单链表带环,求出环的入口
LinkNode* LinklistEnter(LinkNode* head);

//判定两个两个链表是否相交,若相交,返回交点,假设不带环
LinkNode* LinklistHasCross(LinkNode* head1, LinkNode* head2);

Linklist.c

#include "Linklist.h"

//函数声明,因为大多数基本操作在文章刚开始的链接里实现过了,所以大多数基本操作都是直接实现,没有进行头文件声明
void LinklistPushback(LinkNode** phead, Datatype value);
void LinklistPushfront(LinkNode** phead, Datatype value);


//创建节点
LinkNode* Creat() {
    LinkNode* node = malloc(sizeof(LinkNode));
    return node;
}

//销毁节点
void Destroy(LinkNode* node) {
    //非法输入
    if(node == NULL) {
        perror("Destroy");
        exit(1);
    }
    free(node);
}


//初始化节点
void LinklistInit(LinkNode** phead) {
    //非法输入
    if(phead == NULL) {
        perror("Init");
        exit(1);
    }
    *phead = NULL;

}


//逆序打印链表
void LinklistReversePrint(LinkNode* head) {
    //空链表
    if(head == NULL) {
        printf("Empty linklist");
        return;
    }

    LinkNode* cur = head;
    if(cur->next != NULL) {
        LinklistReversePrint(cur->next);
    }

    printf("%c\n", cur->data);
}


//不允许遍历链表,在pos前插入节点
void LinklistInsertBefore(LinkNode* head, LinkNode* pos, Datatype value) {
    //空链表
    if(head == NULL) {
        return;
    }

    //创建新节点 
    LinkNode* node = Creat();
    node->data = pos->data;
    node->next = pos->next;
    pos->next = node;
    pos->data = value;

}


//约瑟夫环
LinkNode* LinklistJosephCircle(LinkNode** phead, int step) {
    //非法输入
    if(phead == NULL) {
        perror("JosephCircle");
        exit(1);
    }
    //空链表
    if(*phead == NULL) {
        return;
    }

    LinkNode* cur = *phead;
    LinkNode* pre = *phead;
    while(cur->next != cur) {
        int count = step;
        while(count--) {
            pre = cur;
            cur = cur->next;    
        }
        LinkNode* tmp = cur;
        printf("remove %c\n", tmp->data);
        pre->next = cur->next;
        cur = pre->next;
        Destroy(tmp);
    }
    return cur;
}


//单链表逆置
/*
1.头插
定义一个指针cur指向当前节点,定义一个指针tmp分离cur指向的当前节点,然后cur后移,将tmp->next指向phead,然后将phead指向tmp
2.就地逆置
定义两个指针cur,pre,第一次cur跳过phead,每次将cur->next指向pre,然后一直后移cur,pre,最后将phead->next = NULL,phead指向pre
*/
void LinklistReverse(LinkNode** phead) {
    //非法输入
    if(phead == NULL) {
        perror("reverse");
        exit(1);
    }
    //空链表
    if(*phead == NULL) {
        return;
    }
    //一个节点
    if((*phead)->next == NULL) {
        return;
    }

    LinkNode* cur = *phead;
    cur = cur->next;
    (*phead)->next = NULL;
    while(cur != NULL) {
        LinkNode* tmp = cur;
        cur = cur->next;
        tmp->next = *phead;
        *phead = tmp;
    }

}
void LinklistReverse2(LinkNode** phead) {
    //非法输入
    if(phead == NULL) {
        perror("reverse2");
        exit(1);
    }
    //空链表
    if(*phead == NULL) {
        return;
    }
    //一个节点
    if((*phead)->next == NULL) {
        return;
    }
    LinkNode* pre = *phead;
    LinkNode* cur = (*phead)->next;
    while(cur != NULL) {
        LinkNode* tmp = cur->next;
        cur->next = pre;
        pre = cur;
        cur = tmp;
    }
    (*phead)->next = NULL;
    *phead = pre;
}


//单链表冒泡排序
void LinklistBubbleSort(LinkNode* head) {
    //非法输入
    if(head == NULL) {
        return;
    }
    //一个节点
    if(head->next == NULL) {
        return;
    }

    LinkNode* cur = head;
    LinkNode* next = head;
    int count = 0;
    while(cur != NULL) {
        cur = cur->next;
        count++;
    }

    int i;
    for(i=0; i<count; i++) {
        int j;
        for(j=0; j<count-1-i; j++) {
            cur = next;
            next = next->next;
            if(cur->data > next->data) {
                Datatype tmp = cur->data;
                cur->data = next->data;
                next->data = tmp;
            }
        }
        cur = next  = head;
    }
}


//将两个有序链表合并成一个有序链表
LinkNode* LinklistMerge(LinkNode* head1, LinkNode* head2) {
    //第一个链表为空链表,若两个都为空,返回head2相当于返回空
    if(head1 == NULL) {
        return head2;
    }
    //第二个链表为空链表
    if(head2 == NULL) {
        return head1;
    }   
    //创建新链表
    LinkNode* head = Creat();
    LinklistInit(&head);
    LinkNode* cur1 = head1;
    LinkNode* cur2 = head2;
    while(cur1!=NULL && cur2!=NULL) {
        if(cur1->data >= cur2->data) {
            LinklistPushback(&head, cur2->data);
            LinkNode* tmp = cur2;
            cur2 = cur2->next;
            Destroy(tmp);
        }else {
            LinklistPushback(&head, cur1->data);
            LinkNode* tmp = cur1;
            cur1 = cur1->next;
            Destroy(tmp);
        }
    }

    //得到最后一个节点
    LinkNode* end = head;
    while(end->next != NULL) {
        end = end->next;
    }

    if(cur2 != NULL) {
        end->next = cur2;
    }
    if(cur1 != NULL) {
        end->next = cur1;
    }
    return head;
}


//查找单链表的中间节点
LinkNode* LinklistFindMidNode(LinkNode* head) {
    //非法输入
    if(head == NULL) {
        return NULL;
    }
    //一个节点
    if(head->next == NULL) {
        return head;
    }

    LinkNode* fast = head;
    LinkNode* slow = head;
    while(fast->next!=NULL && fast->next->next!=NULL) {
        fast = fast->next->next;
        slow = slow->next;
    }
    return slow;
}


//寻找倒数第k个节点
LinkNode* LinklistFindLastKNode(LinkNode* head, size_t k) {
    //非法输入
    if(head == NULL) {
        return NULL;
    }

    LinkNode* fast = head;
    LinkNode* slow = head;

    while(k--){
        if(fast == NULL) {
            return NULL;
        }   
        fast = fast->next;
    }
    while(fast != NULL) {
        fast = fast->next;
        slow = slow->next;
    }
    return slow;
}


//删除倒数第k个节点
void LinklistRemoveLastKNode(LinkNode** phead, size_t k) {
    //非法输入
    if(phead == NULL) {
        perror("RemoveLastKNode");
        exit(1);
    }
    //链表为空
    if((*phead) == NULL) {
        return;
    }

    LinkNode* to_remove = LinklistFindLastKNode(*phead, k);
    //得到了删除节点
    //1.节点是最后一个,得遍历到前一个
    if(to_remove->next == NULL) {
        LinkNode* cur = *phead;
        while(cur->next != to_remove) {
            cur = cur->next;
        }
        cur->next = NULL;
        Destroy(to_remove);
        return;
    }
    //2.节点是第一个和普通个,偷天换日
    LinkNode* tmp = to_remove->next;
    to_remove->data = tmp->data;
    to_remove->next = tmp->next;
    Destroy(tmp);
}


//判断单链表是否带环,带环返回1
int LinklistHasCircle(LinkNode* head) {
    //空链表
    if(head == NULL) {
        return 0;
    }

    LinkNode* fast = head->next;
    LinkNode* slow = head;
    while(fast!=slow && fast->next!=NULL && fast->next->next!=NULL ) {
        fast = fast->next->next;
        slow = slow->next;
    }
    if(fast == slow) {
        return 1;
    }else {
        return 0;
    }
}


//如果链表带环,返回环长度
size_t LinklistGetCircleLength(LinkNode* head) {
    //空链表
    if(head == NULL) {
        return 0;
    }
    //如果链表带环
    int flag = LinklistHasCircle(head);
    if(flag == 1) {
        printf("Linklist has circle, can not count\n");
        return -1;
    }

    LinkNode* cur = head;
    size_t count = 0;
    while(cur != NULL) {
        cur = cur->next;
        count++;
    }
    return count;
}


//如果单链表带环,求出环的入口
LinkNode* LinklistEnter(LinkNode* head) {
    //空链表
    if(head == NULL) {
        return 0;
    }
    LinkNode* fast = head->next;
    LinkNode* slow = head;

    while(fast != slow) {
        fast = fast->next->next;
        slow = slow->next;
    }
    //相遇点
    LinkNode* MeetNode = fast;
    LinkNode* step = head;
    while(step == MeetNode) {
        step = step->next;
        MeetNode = MeetNode->next;
    }
    return step;

}


//判定两个两个链表是否相交,若相交,返回交点,假设不带环
LinkNode* LinklistHasCross(LinkNode* head1, LinkNode* head2) {
    //链表为空
    if(head1 == NULL) {
        perror("Has cross head1 is NULL\n");
        return NULL;
    }
    if(head2 == NULL) {
        perror("Has cross head1 is NULL\n");
        return NULL;
    }
    //两个链表相同
    if(head1 == head2) {
        return head1;
    }

    size_t len1 = 0;        
    size_t len2 = 0;        
    LinkNode* cur1 = head1;
    LinkNode* cur2 = head2;
    while(cur1 != NULL) {
        cur1 = cur1->next;
        len1++;
    }
    while(cur2 != NULL) {
        cur2 = cur2->next;
        len2++;
    }
    //相交
    if(cur2 == cur1) {
        cur1 = head1;
        cur2 = head2;
        if(len1 > len2) {
            int count = len1-len2;
            while(count--) {
                cur1 = cur1->next;
            }   
            while(cur1 != cur2) {
                cur1 = cur1->next;
                cur2 = cur2->next;
            }
            return cur2;
        } else {
            int count = len2-len1;
            while(count--) {
                cur2 = cur2->next;
            }   
            while(cur1 != cur2) {
                cur1 = cur1->next;
                cur2 = cur2->next;
            }
            return cur2;
        }
    } else {
        return NULL;
    }
}


/************************************************
 ******************  Test  **********************
 ************************************************/

#define FUNCTION() printf("=================================== %s ===================================\n",\
                            __FUNCTION__ )

/************************************************
 ****************** 辅助单元 *********************
 ************************************************/


//链表尾插
void LinklistPushback(LinkNode** phead, Datatype value) {
    //非法输入
    if(phead == NULL) {
        perror("LinklistPushback");
        return;
    }
    //空链表
    if(*phead == NULL) {
        *phead = Creat();
        (*phead)->data = value;
        (*phead)->next = NULL;
        return;
    }

    //创建新节点
    LinkNode* new = Creat();
    new->data = value;
    new->next = NULL;
    LinkNode* cur = *phead;
    while(cur->next != NULL) {
        cur = cur->next;
    }
    cur->next = new;
    return;
}

//链表头插
void LinklistPushfront(LinkNode** phead, Datatype value) {
    //输入空指针
    if(phead == NULL) {
        perror("LinklistPushfront");
        return;
    }
    //创建新节点
    LinkNode* node = Creat();
    LinkNode* cur = *phead;
    node->data = value;
    *phead = node;
    node->next = cur;
}

void printChar(LinkNode* head, const char* msg) {
    //空链表
    if(head == NULL) {
        printf("Empty linklist");
        return;
    }

    printf("%s \n", msg);
    LinkNode* cur = head;
    while(cur != NULL) {
        printf("[%c | %p ] ", cur->data, cur);
        cur = cur->next;
    }
    printf("\n");
}



/*********
 *测试单元
 *********/


//逆序打印链表测试
void TestReversePrint() {
    FUNCTION();
    LinkNode* head;
    LinklistInit(&head);
    LinklistPushback(&head, 'a');   
    LinklistPushback(&head, 'b');   
    LinklistPushback(&head, 'c');   
    LinklistPushback(&head, 'd');   

    //printChar(head, "haha");
    LinklistReversePrint(head);
}


//不遍历链表在pos前插入
void TestInsertBefore() {
    FUNCTION();
    LinkNode* head;
    LinklistInit(&head);
    LinklistPushback(&head, 'a');   
    LinklistPushback(&head, 'b');   
    LinklistPushback(&head, 'c');   
    LinklistPushback(&head, 'd');   
    printChar(head,"before test");

    LinklistInsertBefore(head, head, 'x');
    printChar(head,"insert before a");

}


//约瑟夫环测试
void TestJosephCircle() {
    FUNCTION();
    LinkNode* head;
    LinklistInit(&head);
    //构建一个四个元素的环
    LinklistPushback(&head, 'a');   
    LinklistPushback(&head, 'b');   
    LinklistPushback(&head, 'c');   
    LinklistPushback(&head, 'd');   
    head->next->next->next->next = head;

    LinkNode* ret = LinklistJosephCircle(&head, 5);
    printf("%c\n", ret->data);  

}


//头插逆置测试
void TestReverse(void) {
    FUNCTION();
    LinkNode* head;
    LinklistInit(&head);
    LinklistPushback(&head, 'a');   
    LinklistPushback(&head, 'b');   
    LinklistPushback(&head, 'c');   
    LinklistPushback(&head, 'd');   
    printChar(head,"before test");

    LinklistReverse(&head);
    printChar(head,"reverse");
}


//就地逆置测试
void TestReverse2(void) {
    FUNCTION();
    LinkNode* head;
    LinklistInit(&head);
    LinklistPushback(&head, 'a');   
    LinklistPushback(&head, 'b');   
    LinklistPushback(&head, 'c');   
    LinklistPushback(&head, 'd');   
    printChar(head,"before test");

    LinklistReverse2(&head);
    printChar(head,"reverse2");
}


//冒泡排序测试
void TestBubbleSort() {
    FUNCTION();
    LinkNode* head;
    LinklistInit(&head);
    LinklistPushback(&head, 'g');   
    LinklistPushback(&head, 'r');   
    LinklistPushback(&head, 'a');   
    LinklistPushback(&head, 'g');   
    LinklistPushback(&head, 'l');   
    LinklistPushback(&head, 'e');   
    LinklistPushback(&head, 'v');   
    LinklistPushback(&head, 't');   
    printChar(head,"before test");

    LinklistBubbleSort(head);
    printChar(head,"before test");

}


//把两个有序量表拼成一个有序链表测试
void TestMerge() {
    FUNCTION();

    LinkNode* head1;
    LinklistInit(&head1);
    LinklistPushback(&head1, 'a');  
    LinklistPushback(&head1, 'b');  
    LinklistPushback(&head1, 'c');  
    LinklistPushback(&head1, 'd');  
    printChar(head1,"before test");

    LinkNode* head2;
    LinklistInit(&head2);
    LinklistPushback(&head2, 'a');  
    LinklistPushback(&head2, 'b');  
    LinklistPushback(&head2, 'c');  
    LinklistPushback(&head2, 'd');  
    printChar(head2,"before test");

    LinkNode* head = LinklistMerge(head1, head2);
    printChar(head, "after merge");
}


//寻找中间节点测试
void TestFindMidNode() {
    FUNCTION();
    LinkNode* head;
    LinklistInit(&head);
    LinklistPushback(&head, 'a');   
    LinklistPushback(&head, 'b');   
    LinklistPushback(&head, 'c');   
    LinklistPushback(&head, 'd');   
    LinklistPushback(&head, 'e');   
    printChar(head,"before test");

    LinkNode* ret = LinklistFindMidNode(head);
    printf("%c\n", ret->data);
}


//寻找倒数第k个节点测试
void TestFindLastKNode() {
    FUNCTION();
    LinkNode* head;
    LinklistInit(&head);
    LinklistPushback(&head, 'a');   
    LinklistPushback(&head, 'a');   
    LinklistPushback(&head, 'b');   
    LinklistPushback(&head, 'c');   
    LinklistPushback(&head, 'd');   
    LinklistPushback(&head, 'e');   
    LinklistPushback(&head, 'b');   
    LinklistPushback(&head, 'c');   
    LinklistPushback(&head, 'd');   
    LinklistPushback(&head, 'e');   
    printChar(head,"before test");

    LinkNode* ret = LinklistFindLastKNode(head, 1);
    printf("%c\n", ret->data);
}


//删除倒数第k个节点测试
void TestRemoveLastKNode() {
    FUNCTION();
    LinkNode* head;
    LinklistInit(&head);
    LinklistPushback(&head, 'a');   
    LinklistPushback(&head, 'a');   
    LinklistPushback(&head, 'b');   
    LinklistPushback(&head, 'c');   
    LinklistPushback(&head, 'd');   
    LinklistPushback(&head, 'e');   
    LinklistPushback(&head, 'b');   
    LinklistPushback(&head, 'c');   
    LinklistPushback(&head, 'd');   
    LinklistPushback(&head, 'e');   
    printChar(head,"before test");

    LinklistRemoveLastKNode(&head, 1);
    printChar(head, "remove last 1");
}


//判断链表是否带环测试
void TestHasCircle() {
    FUNCTION();
    LinkNode* head;
    LinklistInit(&head);
    //构建一个两元素的环
    LinklistPushback(&head, 'a');   
    LinklistPushback(&head, 'b');   
    //LinklistPushback(&head, 'c'); 
    //LinklistPushback(&head, 'd'); 
//  head->next->next = head;

    int ret = LinklistHasCircle(head);
    if(ret == 1) {
        printf("带环\n");
    }else {
        printf("不带环\n");
    }
}


//链表长度测试
void TestGetCircleLength() {
    FUNCTION();
    LinkNode* head;
    LinklistInit(&head);
//  LinklistPushback(&head, 'a');   
//  LinklistPushback(&head, 'a');   
//  LinklistPushback(&head, 'b');   
//  LinklistPushback(&head, 'c');   
//  LinklistPushback(&head, 'd');   
//  LinklistPushback(&head, 'e');   
//  LinklistPushback(&head, 'b');   
//  LinklistPushback(&head, 'c');   
//  LinklistPushback(&head, 'd');   
//  LinklistPushback(&head, 'e');   
//  printChar(head,"before test");
    LinklistPushback(&head, 'a');   
    LinklistPushback(&head, 'b');   
    LinklistPushback(&head, 'c');   
    LinklistPushback(&head, 'd');   
    head->next->next->next->next = head;

    size_t ret = LinklistGetCircleLength(head);
    printf("The linklist length is %d\n", ret);
}


//环入口点测试
void TestEnter() {
    //构建四元环
    FUNCTION();
    LinkNode* head;
    LinklistInit(&head);
    LinklistPushback(&head, 'a');   
    LinklistPushback(&head, 'b');   
    LinklistPushback(&head, 'c');   
    LinklistPushback(&head, 'd');   
    head->next->next->next->next = head;

    LinkNode* ret = LinklistEnter(head);
    printf("The actual enter address is %p, function return address is %p\n", head, ret);
}


//判定两个两个链表是否相交(假设不带环)测试
void TestHasCross() {
    FUNCTION();

    LinkNode* head1;
    LinklistInit(&head1);
    LinklistPushback(&head1, 'a');  
    LinklistPushback(&head1, 'b');  
    LinklistPushback(&head1, 'c');  

    LinkNode* head2;
    LinklistInit(&head2);
    LinklistPushback(&head2, 'a');  
    LinklistPushback(&head2, 'b');  

    LinkNode* head3;
    LinklistInit(&head3);
    LinklistPushback(&head3, 'a');  
    LinklistPushback(&head3, 'b');  
    LinklistPushback(&head3, 'c');  
    LinklistPushback(&head3, 'd');  
    //创建相交点
    head1->next->next->next = head3;
    head2->next->next = head3;

    printChar(head1,"head1");
    printChar(head2,"head2");

    LinkNode*ret = LinklistHasCross(head1, head2);
    printf("The actual meet node address is %p, function return address is %p\n", head3, ret);
}


int main() {
    TestReversePrint();
    TestInsertBefore();
    TestJosephCircle();
    TestReverse();
    TestReverse2();
    TestBubbleSort();
    TestMerge();
    TestFindMidNode();
    TestFindLastKNode();
    TestRemoveLastKNode();
    TestHasCircle();
    TestGetCircleLength();
    TestEnter();
    TestHasCross();
}

本文用例均实现在Centos 6.5, 如有纰漏,请斧正

展开阅读全文

没有更多推荐了,返回首页