链表回文判断

最近开始学习算法,在这里记录心得体会。今日体会是,用数学语言描述算法,会比较清晰。

/*  理解快慢指针:
 *      1. 给链表的所有节点一个编号: 长度为len的链表, 节点编号依次为:
 *         0,1,...,len-1
 *      2. 关于中点:
 *         2.1. 如果链表长度为0, 上述编号失效, 因为len-1=-1没有意义;
 *         2.2. 如果链表长度为奇数2n+1, 那么中点为n;
 *         2.3. 如果链表长度为偶数2n, 那么中点为(2n-1)/2=n-0.5,
 *              根据需要选择n-1或n;
 *      3. 快慢指针的移动:
 *         3.1. slow: 慢指针每次移动1步
 *         3.2. fast: 快指针每次移动2步
 *      4. 根据需要推导算法:
 *         4.0. 基本思路:
 *              快指针主动移动, 慢指针跟随移动;
 *              结束时, 快指针移动2n, 慢指针移动n;
 *              根据起点不同控制慢指针结束时所在位置;
 *         4.1. 需求1: 偶数时指向中间靠右, 中点选择n而不是n-1
 *              slow: 从0开始, 结束时为n
 *              fast: 从0开始, 结束时为2n
 *              如果len=2n, 则慢指针指向右侧(选择了n而不是n-1);
 *              如果len=2n+1, 则慢指针指向中间, n=(0+2n)/2为中点;
 *         4.2. 需求2: 偶数时指向中间靠左, 中点选择n-1而不是n
 *              思路: slow慢一步, 或者fast快一步;
 *                    写代码时让fast快一步更好实现
 *              slow: 从0开始, 结束时为n
 *              fast: 从1开始, 结束时为2n+1; (如果长度小于2则提前结束)
 *              如果len=2n+1, 则n为中点;
 *              如果长度为2n+2, 因为fast先走了一步, 所以只会移动2n步,
 *                  则slow=n为左侧点(0+2n+1)/2=n+0.5, 左侧为n, 右侧为n+1
 */

node_t*
get_mid(node_t *list, int sel_left)
{
    if (!list)
        return (list);
    
    node_t *fast = sel_left ? list->next : list, *slow = list;
    while (fast && fast->next) {
        slow = slow->next;
        fast = fast->next->next;
    }
    
    return (slow);
}

/*
 *  回文判断思路:
 *      1. 如果链表长度为奇数, 中点不需要判断;
 *         如果链表长度为偶数, 则要求左右对称
 *      2. 步骤:
 *         2.1. 找到并记录链表中点位置: 奇数长度返回中点, 偶数长度返回左侧点;
 *         2.2. 把中点右侧链表反转;
 *         2.3. 左右链表按照顺序比较各个节点, 右侧链表长度小于或等于左侧,
 *              遇到NULL则说明相等;
 *         2.4. 在比较的同时, 完成链表的再次反转;
 *         2.5. 重新拼接链表.
 */

static inline void
move_node(node_t **pdst, node_t **psrc)
{
    node_t *next = (*psrc)->next;
    (*psrc)->next = *pdst;
    *pdst = *psrc;
    *psrc = next;
}

int
is_parlindrome(node_t *list)
{
    // 空链表认为是回文
    if (!list)
        return (1);
    
    // 找到链表中点或中点偏左
    node_t *fast = list->next, *mid = list;
    while (fast && fast->next) {
        mid = mid->next;
        fast = fast->next->next;
    }
    
    // 断开右侧链表
    node_t *tmp = mid->next;
    mid->next = NULL;
    
    // 反转右侧链表
    node_t *right = NULL;
    while (tmp)
        move_node(&right, &tmp);
    
    // 比较左右链表, 同时再次反转右侧链表, 右侧链表长度一定不大于左侧
    node_t *left = list;
    tmp = right;
    right = NULL;
    
    int yes = 1;
    while (tmp && yes) {
        yes = tmp->value == left->value;
        left = left->next;
        move_node(&right, &tmp);
    }
    
    while (tmp)
        move_node(&right, &tmp);
    
    // 重新拼接链表
    mid->next = right;
    return (yes);
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小瓶子36

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值