我面试遇到的C语言中单链表相关的操作粗浅实现

主要包括单链表中节点的插入, 删除, 反转链表, 链表排序, 查询链表中第几个或倒数第几个节点的值(即将补充的有链表是否有环, 两个链表是否相交以及第一个公共节点). 仅用于备注, 不断更新, 代码如下:

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

typedef struct _LinkedNodeTAG {
    char value;
    struct _LinkedNodeTAG *next;
} LinkedNode;

LinkedNode *new_LinkedNode(char value) {
    LinkedNode *pNode;
    pNode = (LinkedNode *)malloc(sizeof(LinkedNode));
    pNode->value = value;
    return pNode;
}

LinkedNode *new_LinkedNode_ex(char value, LinkedNode *next) {
    LinkedNode *pNode;
    pNode = (LinkedNode *)malloc(sizeof(LinkedNode));
    pNode->value = value;
    pNode->next = next;
    return pNode;
}

void iter_print(LinkedNode *head) {
    while (head) {
        printf("%c ", head->value);
        head = head->next;
    }
    printf("\n");
}

/* 获取链表上索引位置为index的节点, 需要考虑到链表头节点是否为NULL, 链表是否只有一个节点, 索引是否大于链表的长度. 
 * 这里设定索引从0开始, 索引为正将从头节点向前搜索, 如果为负表示倒数第index个节点, 比如-1表示倒数第一个, 即尾节点.
 * 求倒数第n个节点的值的思路是使用两个指针从头结点开始一起遍历, 保证指针间隔为n, 第一个指针遍历完时, 后面的指针正好指在倒数第n个节点上 */
LinkedNode *get_node_at(LinkedNode *head, int index) {
    if (index >= 0) {/* -0也是0 */
        while (index-- > 0 && head) {/* 两个出口, 抵达索引位置或者链表已遍历完(索引位置还未到达) */
            head = head->next;
        }
        return head;
    } else {
        index = -index;
        LinkedNode *node = head;
        /* 这里其实可以只用一个循环, 但是每次循环都做判断 */
        while (index-- > 0 && head) {
        head = head->next;
        }
        if (index > 0) return NULL;
        while (head) {
            head = head->next;
            node = node->next;
        }
        return node;
    }
}

int remove_node(LinkedNode *head, LinkedNode *node) {
    if (!head || !node) return -1;
    if (head == node && !head->next) {/* 既是头节点又没有下一个节点, 即只有一个节点 */
        free(head);
        head = NULL;
        return 0;
    }
    if (head != node && !node->next) {/* 有多个节点, 要删除的可能是尾节点的情况 */
        while (head && head->next != node) head = head->next; 
        if (!head) {/* node可能不在链表上 */
            return -1;
        } else if (head->next == node) {/* 此时head即为倒数第二个节点 */
            head->next = NULL;
            free(node);
            node = NULL;
        }
        return 0;   
    }
    /* 有多个节点, 且要删除的不是尾节点的情况, 如果node在另一个链表上, 责任留给调用者. 这里思路是:
     * 将要删除的节点node的下一个元素的内容value拷贝到要node上, 将node的next指向原下一个元素的next指向的节点, 然后删除原下一个元素节点 */
    LinkedNode *next = node->next;
    node->value = next->value;
    node->next = next->next;
    free(next);
    next = NULL;
    return 0;
}

/* 一个链表, 在node节点前插入inserting节点  */
int insert_node_before(LinkedNode *head, LinkedNode *node, LinkedNode *inserting) {
    if (!head || !node || !inserting) return -1;
    if (head == node) {
        inserting->next = head; /* 链表head节点发生改变 */
        return 0;
    }
    /* 有多个节点, 如果node在另一个链表上, 责任留给调用者. 这里思路是:
     * 交换node和inserting的内容value, 使原node变成inserting, inserting变成node, 然后node的next原指向的节点由inserting指向, node的next指向inserting */
    char temp_value = node->value;
    node->value = inserting->value;
    inserting->value = temp_value;

    inserting->next = node->next;
    node->next = inserting;
    return 0;
}

/* 首先不断递归进去, 到倒数第二个节点后, 通过next的next, 向前指向前一个节点, 并去掉指向后一个节点但指针 */
LinkedNode *reverse_recusive(LinkedNode *head) {
    if (!head || !head->next) return head;
    LinkedNode *newHead = reverse_recusive(head->next);
    head->next->next = head;
    head->next = NULL;
    return newHead;
}

LinkedNode *reverse_loop(LinkedNode *head) {
    if (!head) return head;
    LinkedNode *prev, *next, *remember; /* 使用三个指针 */
    prev = head;
    next = head->next;
    prev->next = NULL; /* 第一个作为尾节点, 置空其next指针  */
    while(next) {
        remember = next->next; /* 记住下一个的下一个, 以防将下一个但next指向自己以后链表断掉 */
        next->next = prev; /* 反转 */
        /* 向前移动指针  */
        prev = next; 
        next = remember;
    }
    return prev;
}

/* 选定一个pivot, 这里是begin, 使用两个指针, big指针往前走, 如果发现指向的值比pivot小, 则向前移动small指针一步, 并交换这两个指针指向的节点内容value. 
 * 这样, small左边到pivot所有节点的内容比pivot指向的节点内容小, 而big左边到small所有节点的内容比pivot指向的节点内容大, 符合了快速排序中一种partition实现思路.
 * 遍历一遍链表后把pivot指向的节点内容与small指针交换, 保证此时的pivot前面的比它小, 后面的比它大. */
LinkedNode *partition(LinkedNode *begin, LinkedNode *end, LinkedNode *pivot) {
    LinkedNode *small, *big;
    if (!begin || !pivot) {
        return NULL;
    }
    small = big = begin;
    while(big != end) {
        if (big->value < pivot->value) {
            small = small->next;

            char temp = big->value;
            big->value = small->value;
            small->value = temp;
        }
        big = big->next;
    }
    char temp = pivot->value;
    pivot->value = small->value;
    small->value = temp;
    return small;
}

void quick_sort(LinkedNode *begin, LinkedNode *end) {
    if (begin != end) {
        LinkedNode * pivot = partition(begin, end, begin);
        quick_sort(begin, pivot);
        quick_sort(pivot->next, end);
    }
}

int main(void) {
    LinkedNode *head;
    LinkedNode *a = new_LinkedNode('e');
    LinkedNode *b = new_LinkedNode('f');
    LinkedNode *c = new_LinkedNode('b');
    LinkedNode *d = new_LinkedNode('a');
    LinkedNode *e = new_LinkedNode('d');
    LinkedNode *f = new_LinkedNode('g');
    LinkedNode *g = new_LinkedNode('c');
    a->next = b;
    b->next = c;
    c->next = d;
    d->next = e;
    e->next = f;
    f->next = g;
    g->next = NULL;
    head = a;
    iter_print(head);

    //LinkedNode *node = new_LinkedNode('m');
    //insert_node_before(head, c, node);
    //iter_print(head);

    //remove_node(head, d);
    //iter_print(head);

    //head = reverse_recusive(head);
    //iter_print(head);

    //head = reverse_loop(head);
    //iter_print(head);

    //partition(head, head, NULL, head);
    //iter_print(head);

    //quick_sort(head, NULL);
    //iter_print(head);

    //LinkedNode *t = get_node_at(head, -5);
    //if (!t) {
    //      printf("NULL\n");
    //} else {
    //      printf("%c\n", t->value);
    //}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值