[这是个无比吸引人的标题+文末彩蛋]

简介: [这是个无比吸引人的标题+文末彩蛋]

简说Python,号主老表,Python终身学习者,数据分析爱好者,从18年开始分享Python知识,原创文章227篇,写过Python、SQL、Excel入门文章,也写过Web开发、数据分析文章,老表还总结整理了一份2022Python学习资料和电子书资源,关注后私信回复:2022 即可领取。

今天给大家分享的书籍《Python程序员面试算法宝典》第一章第七小节:翻转链表相邻结点和第一章第八节:翻转链表k个相邻结点。

如果你是第一次看,也许,你可以看看本系列下面的文章:

Smaller And Smarter Python数据结构:链表逆转Smaller And Smarter Python数据结构:删除无序链表重复结点Smaller And Smarter Python数据结构:链表相加Smaller And Smarter Python数据结构:链表进行重新排序Smaller And Smarter Python数据结构:链表倒数第K个元素+检测单链表环算法


今日问题 :翻转链表相邻结点

"""
目标:写一段程序,翻转链表相邻结点
例如:
输入-> 1->2->3->4->5
输出-> 2->1->4->3->5
Goal: write a program to flip the adjacent nodes of the linked list
For example:
Input  - > 1 - > 2 - > 3 - > 4 - > 5
Output - > 2 - > 1 - > 4 - > 3 - > 5
"""

解题准备

首先我们写好链表的基本操作,在a_0_base.py文件中,目前包含对链表的定义类,初始化函数,遍历函数。(带头结点)

# -*- coding: utf-8 -*-
"""
@author = 老表
@date = 2019-10-19
@个人微信公众号 : 简说Python
"""
# 链表数据结构定义
class ListNode:
    def __init__(self, x):
        self.data = x
        self.next = None
class ListOperation:
    # 根据链表数据初始化链表
    @staticmethod
    def init_list(n_list):
        # 初始化一个头指针
        head = ListNode("head")
        cur = head
        for i in n_list:
            node = ListNode(i)
            cur.next = node
            cur = node  # 相当于 cur = cur.next,后移
        return head
    # 遍历链表,带头结点
    @staticmethod
    def ergodic_list(head):
        cur = head.next
        while cur:
            print(cur.data)
            cur = cur.next
       # 获取链表长度
    @staticmethod
    def get_len_list(head):
        cur = head.next
        len_list = 0
        while cur:
            len_list = len_list + 1
            cur = cur.next
        return len_list

解题

开始程序前需提前导入前面写好的链表基本操作包和结点数据结构,在Linked_list的a_0_base.py中。

from Linked_list.a_0_base import ListOperation

方法一:遍历交换法

解题思路

"""
Method One : 遍历交换法
核心思想:遍历链表,记录从往后,记录相邻的两个结点,
改变结点指向,实现交换(实际需要四个结点,比如:
a->b->c->d  b,c 交换,要修改三处链表指向
故a,b,c,d四个结点都要记录)。
时间复杂度:O(N)
空间复杂度:O(1)
"""

代码

def flip_adjacent_node_one(head):
    if not head.next:  # 空链表
        return head  # 返回头结点
    pre_node = head  # 记录前驱结点
    cur_node = head.next  # 记录当前结点
    while cur_node.next:  # 遍历交换(cur_node 与 cur_node.next交换)
        next_node = cur_node.next.next
        # 记录下一个cur_node,因为相邻交换没两个进行一次,故下一个为cur_node.next.next
        pre_node.next = cur_node.next
        # 1、交换第一步,前驱结点指向cur_node的后一个结点(cur_node.next)
        cur_node.next.next = cur_node
        # 2、交换第二步,cur_node.next反指,指向cur_node
        cur_node.next = next_node
        # 3、交换第三步,cur_node指向cur_node.next.next,完成交换
        """(原本是cur_node.next指向cur_node.next.next,
        但现在cur_node与cur_node.next交换位置,
        故让cur_node指向cur_node。next.next)"""
        pre_node = cur_node
        # 前驱结点后移
        cur_node = next_node
        # 当前结点后移
    return head  # 返回头结点

方法二:递归交换法

解题思路

"""
Method Two :递归交换法
核心思想:遍历链表,每次将相邻结点的第二个结点的
next指针指向前一个结点,然后将第一个结点的指针指
向下一组已经翻转好的相邻结点的第一个结点。
时间复杂度:O(N)
空间复杂度:O(1)
"""

代码

def flip_adjacent_node_two(head):
    if not head:  
    # 空链为空 或者 递归到尾
        return head  
        # 开始回溯,返回结点
    if head.data == "head":  
    # 去掉头结点,直接从第一个结点开始
        head = head.next
    first_node = head  
    # 链表第一个结点
    second_node = head.next  
    # 获取第二个结点
    first_node.next = flip_adjacent_node_two(head.next.next)
    # 递归,传递第三个结点作为参数
    # 回溯时,让第一个结点指向下一组已经翻转好的相邻结点的第一个结点
    second_node.next = first_node
    # 回溯时,让第二个结点反指向第一个结点
    return second_node  # 返回头结点

测试代码

# 当然,也许还有别的方法,比如建一个辅助的链表
# 欢迎你说出你的想法
# 程序入口,测试函数功能
if __name__ == "__main__":
    list_data = [1, 2, 3, 4, 5, 6]  # 链表初始化数据
    head = ListOperation.init_list(list_data)  # 初始化链表,带头结点
    ListOperation.ergodic_list(head)  # 未操作前,遍历打印链表
    # head = flip_adjacent_node_one(head)  # 测试方法一
    head.next = flip_adjacent_node_two(head)  # 测试方法二
    print("---------------------")
    ListOperation.ergodic_list(head)  # 操作后,遍历打印链表

今日问题 :翻转链表k个相邻结点

"""
目标:写一段程序,k个为一组翻转链表相邻结点
例如:
输入 k = 3
输入-> 1->2->3->4->5->6->7
输出-> 3->2->1->6->5->4->7
Goal: write a program, K for a group of adjacent nodes of flip list
For example:
Input k = 3
Input  - > 1 - > 2 - > 3 - > 4 - > 5 - > 6 - > 7
Output - > 3 - > 2 - > 1 - > 6 - > 5 - > 4 - > 7
"""

解题准备

首先我们写好链表的基本操作,在a_0_base.py文件中,目前包含对链表的定义类,初始化函数,遍历函数。(带头结点)

# -*- coding: utf-8 -*-
"""
@author = 老表
@date = 2019-10-19
@个人微信公众号 : 简说Python
"""
# 链表数据结构定义
class ListNode:
    def __init__(self, x):
        self.data = x
        self.next = None
class ListOperation:
    a = 1
    # 根据链表数据初始化链表
    @staticmethod
    def init_list(n_list):
        # 初始化一个头指针
        head = ListNode("head")
        cur = head
        for i in n_list:
            node = ListNode(i)
            cur.next = node
            cur = node  # 相当于 cur = cur.next,后移
        return head
    # 根据链表数据初始化一个有环的链表
    @staticmethod
    def init_ring_list(n_list):
        # 初始化一个头指针
        head = ListNode("head")
        cur = head
        for i in n_list:
            node = ListNode(i)
            cur.next = node
            cur = node  # 相当于 cur = cur.next,后移
        cur.next = head.next.next.next.next  # 随意的,让最后一个结点指向第四个结点
        return head
    # 遍历链表
    @staticmethod
    def ergodic_list(head):
        # print(head.data)
        cur = head.next
        while cur:
            print(cur.data)
            cur = cur.next
    # 获取链表长度
    @staticmethod
    def get_len_list(head):
        cur = head.next
        len_list = 0
        while cur:
            len_list = len_list + 1
            cur = cur.next
        return len_list

解题

开始程序前需提前导入前面写好的链表基本操作包和结点数据结构,在Linked_list的a_0_base.py中。

from Linked_list.a_0_base import ListOperation

解题思路

"""
Method One : 分组逆转法
核心思想:以k个结点为一组,每组看作一个子链表,
逆转每个子链表,然后让逆转后的最后一个结点指向
下一组,继续逆转,直到结束。
时间复杂度:O(N)
空间复杂度:O(1)
"""

辅助函数

'''
在做 链表重新排序 时,我们就已经写个这个函数
'''
# 辅助函数:不带头结点链表逆转
def reverse_list(l1):
    if not l1 or not l1.next:  # 空链表或只有一个结点
        return l1
    cur_node = l1.next  # 记录当前结点
    pre_node = l1  # 记录前驱结点
    l1.next = None  # 设置尾结点
    while cur_node:
        next_node = cur_node.next  # 记录后继结点
        cur_node.next = pre_node  # 当前结点反向指向前驱结点
        pre_node = cur_node  # 前驱结点后移
        cur_node = next_node  # 后移遍历
    return pre_node  # 返回逆转后的第一个结点

功能代码

def flip_k_node_one(head, k):
    if not head.next:
        # 空链表
        return head
        # 返回头结点
    pre_node = head
    # 记录前驱结点
    begin_node = head.next
    # 记录每组开始结点
    while begin_node:
        # 遍历分组逆转,连接
        i = 1
        # 分组标记 i < k
        end_node = begin_node
        # 记录每组尾结点
        while i < k:
            # 遍历找到每组尾结点
            if end_node.next:
                end_node = end_node.next
            else:
                return head
                # 剩余结点不足k个结点,说明分组转换完成
            i = i + 1
            # 标记值后移
        # print("222222", i)
        next_node = end_node.next
        # 记录下一组开始结点
        end_node.next = None
        # 断开前一组与后一组的链接
        pre_node.next = reverse_list(begin_node)
        # 逆转前一组的子链,返回逆转后的表尾结点,让开始结点指向该结点
        # print(pre_node.next.data)
        begin_node.next = next_node
        # 前一组逆转好的尾结点指向下一组的第一个结点
        pre_node = begin_node
        # 前驱结点后移,即成为前一组的尾结点,相当于后一组的头结点
        begin_node = next_node
        # 开始结点后移,成为后一组的第一个结点
    return head  # 返回头结点

测试代码

# 当然,也许还有别的方法,比如建一个辅助的链表
# 欢迎你说出你的想法
# 程序入口,测试函数功能
if __name__ == "__main__":
    list_data = [1, 2, 3, 4, 5, 6, 7]  # 链表初始化数据
    head = ListOperation.init_list(list_data)  # 初始化链表,带头结点
    ListOperation.ergodic_list(head)  # 未操作前,遍历打印链表
    len_list = ListOperation.get_len_list(head)  # 获取链表长度
    k = int(input("请输入参数K in (0, %d] : " % len_list))  # 输入参数k值
    while k > len_list:  # 基本判断,过滤
        print("k值过大,k in (0, %d]" % len_list)
        k = int(input("请重新输入参数K:"))
    head = flip_k_node_one(head, k)  # 测试方法一
    print("---------------------")
    ListOperation.ergodic_list(head)  # 操作后,遍历打印链表

本文代码思路部分来自书籍《Python程序员面试宝典》,书中部分代码有问题或未提供代码,文中已经修改过了,并添加上了丰厚的注释,方便大家学习,后面我会把所有内容开源放到Github上,包括代码,思路,算法图解(手绘或者电脑画),时间充裕的话,会录制视频。

希望大家多多支持。

大家好,我是老表

觉得本文不错的话,转发、留言、点赞,是对我最大的支持。

相关文章
|
5月前
|
Python
京东技术团队撰写的整整986页《漫画学Python》到底有什么魅力?
这是一本Python入门书。无论您是想学习编程的小学生,还是想参加计算机竞赛的中学生,抑或是计算机相关专业的大学生,甚至是正在从事软件开发的职场人,本书都适合您阅读和学习。但您若想更深入地学习Python并进行深层次应用,则需要选择其他相关图书。
|
7月前
|
Python
【分享代码】国庆氛围不能少,快来给头像加个国旗
【分享代码】国庆氛围不能少,快来给头像加个国旗
90 0
|
7月前
|
前端开发 算法 JavaScript
2025年阿里招聘已放出,标题没错,就是2025年
机会总是留给有准备的人,话都懂,但真正做到,你至少领先80%的人,先说一个事,就在昨天,V哥帮公众号里的一个用户远程做了沟通,这位女生是长春某一本学校的在读大三学生,将于2025年毕业,从公众号里找到了V哥,暂且称她为小曦。
136 0
|
前端开发 JavaScript UED
「CSS畅想」何以解忧,美食足矣,用技术给好友开发了一个零食盲盒小游戏
前端技术从业者与非技术好友互动,用技术给好友开发了一个零食盲盒小游戏
268 1
喜迎国庆,几行简单代码即可实现【国庆风格】社交头像
喜迎国庆,几行简单代码即可实现【国庆风格】社交头像
191 0
喜迎国庆,几行简单代码即可实现【国庆风格】社交头像
|
前端开发
#yyds干货盘点 前端歌谣的刷题之路-第七题-语义化标签
#yyds干货盘点 前端歌谣的刷题之路-第七题-语义化标签
146 0
#yyds干货盘点 前端歌谣的刷题之路-第七题-语义化标签
|
前端开发
#yyds干货盘点 前端歌谣的刷题之路-第九题-视频媒体标签属性
#yyds干货盘点 前端歌谣的刷题之路-第九题-视频媒体标签属性
114 0
#yyds干货盘点 前端歌谣的刷题之路-第九题-视频媒体标签属性
|
小程序 数据安全/隐私保护 计算机视觉
切勿外传,我要把我的写作“小心思”放出来了!| 年终总结之学习篇🚩
切勿外传,我要把我的写作“小心思”放出来了!| 年终总结之学习篇🚩
185 0
切勿外传,我要把我的写作“小心思”放出来了!| 年终总结之学习篇🚩
|
数据采集 存储 前端开发
只用 Markdown 就写出好看的简历,在线简历应用闪亮登场!
大家都知道金三银四是招聘的季节,各大互联网公司都开始了春招。最近有很多人来找我帮忙看简历,简历的模板可谓参差不齐,有过分炫酷,还有一些朋友直接把 word 丢了过来,排版就显得比较乱。
只用 Markdown 就写出好看的简历,在线简历应用闪亮登场!
写给普通人看的自媒体分享|万事开头难,勇于分享自己,我们都很棒!
相信自媒体这个词对于我们任何一个人来说都不陌生了,到底什么是自媒体呢?从名称属性来看,很简单,就是自己做自己的媒体,自己将自己的东西分享出来,就是自媒体。那么说的实际一点,对于我们普通人...
216 0
下一篇
DataWorks