join() 连接字符、center() 居中填充

1. 题目考点

这道题主要考:

字符串拼接、列表存储每一行、join() 连接字符、center() 居中填充、循环构造规律、上下对称图形打印。

它不是难在语法,而是难在看出图案规律。


2. 审题

题目给你一个整数 size,让你生成一个字母 rangoli 图案。

例如 size = 3 时,用到字母:

a b c

但是图案从外到内是:

----c----
--c-b-c--
c-b-a-b-c
--c-b-c--
----c----

所以我们要注意几个信息:

第一,最外层是第 size 个字母。
size = 3,最外层是 c
size = 5,最外层是 e

第二,中间一定是 a

第三,图案上下对称。
也就是说,只要我们能生成上半部分加中间行,下半部分可以直接倒过来复用。

第四,每一行左右也对称。
例如:

c-b-a-b-c

左边是:

c-b

中间是:

a

右边是:

b-c

3. 思路提示

先不要急着写代码。我们先找规律。

对于 size = 3,核心内容其实是这三行:

c
c-b-c
c-b-a-b-c

然后再把它们居中补 -,最后再镜像出下半部分。

所以做题方向是:

第一步,准备字母表:

letters = "abcdefghijklmnopqrstuvwxyz"

第二步,生成从顶部到中间的每一行。

对于 size = 3

i = 2 -> c
i = 1 -> c-b-c
i = 0 -> c-b-a-b-c

这里的 i 可以理解成“这一行中间最小的字母下标”。

第三步,把这一行的字母用 - 连接起来。

比如:

["c", "b", "a", "b", "c"]

变成:

c-b-a-b-c

第四步,把这一行放到固定宽度中间,两边用 - 补齐。

第五步,生成下半部分。

因为图案上下对称,所以:

上半部分 + 上半部分去掉最后一行后倒序

4. 完整设计思路

第一步:确定总宽度

中间行最长。

size = 3 为例:

c-b-a-b-c

这里有:

字母数量:2 * size - 1 = 5
横杠数量:2 * size - 2 = 4
总宽度:5 + 4 = 9

所以总宽度公式是:

width = 4 * size - 3

例如:

size = 3 -> width = 9
size = 5 -> width = 17

第二步:生成每一行的字母

假设:

letters = "abcdefghijklmnopqrstuvwxyz"
size = 3

我们需要用到:

letters[0] = a
letters[1] = b
letters[2] = c

从顶部到中间,isize - 1 递减到 0

for i in range(size - 1, -1, -1):

size = 3 时:

i = 2
i = 1
i = 0

对应三行:

i = 2 -> c
i = 1 -> c-b-c
i = 0 -> c-b-a-b-c

生成一行时,可以分成左半部分和右半部分。

比如 i = 0 时:

左半部分:c b
中间及右半部分:a b c
合起来:c b a b c

第三步:用 join() 加横杠

如果我们有一个列表:

row_letters = ["c", "b", "a", "b", "c"]

可以用:

"-".join(row_letters)

得到:

c-b-a-b-c

第四步:用 center() 居中补横杠

例如:

"c".center(9, "-")

结果是:

----c----

也就是说,center(总宽度, 填充字符) 会让字符串居中,不够的位置用指定字符补齐。

这里我们要用:

row.center(width, "-")

5. 代码实现

如果题目模板要求你写 print_rangoli(size),可以这样写:

def print_rangoli(size):
    letters = "abcdefghijklmnopqrstuvwxyz"
    lines = []
    width = 4 * size - 3

    for i in range(size - 1, -1, -1):
        row_letters = []

        # 左半部分:从最大的字母往当前字母前一个走
        for j in range(size - 1, i, -1):
            row_letters.append(letters[j])

        # 中间和右半部分:从当前字母往最大的字母走
        for j in range(i, size):
            row_letters.append(letters[j])

        row = "-".join(row_letters)
        row = row.center(width, "-")
        lines.append(row)

    # 下半部分是上半部分去掉中间行之后的倒序
    bottom_lines = lines[-2::-1]

    all_lines = lines + bottom_lines

    print("\n".join(all_lines))

如果你的平台要求函数 return 一个字符串,而不是直接 print,可以写成:

def rangoli(size):
    letters = "abcdefghijklmnopqrstuvwxyz"
    lines = []
    width = 4 * size - 3

    for i in range(size - 1, -1, -1):
        row_letters = []

        for j in range(size - 1, i, -1):
            row_letters.append(letters[j])

        for j in range(i, size):
            row_letters.append(letters[j])

        row = "-".join(row_letters)
        row = row.center(width, "-")
        lines.append(row)

    bottom_lines = lines[-2::-1]
    all_lines = lines + bottom_lines

    return "\n".join(all_lines)

如果题目最后有类似:

if __name__ == '__main__':
    n = int(input())
    print_rangoli(n)

你就保留 print_rangoli(size) 版本即可。


6. 运行演示:以 size = 3 为例

首先:

letters = "abcdefghijklmnopqrstuvwxyz"
width = 4 * 3 - 3

所以:

width = 9

循环从:

i = 2, 1, 0

开始。

i = 2 时:

row_letters = ["c"]

连接后:

c

居中后:

----c----

i = 1 时:

row_letters = ["c", "b", "c"]

连接后:

c-b-c

居中后:

--c-b-c--

i = 0 时:

row_letters = ["c", "b", "a", "b", "c"]

连接后:

c-b-a-b-c

本身已经是宽度 9,所以不需要补横杠:

c-b-a-b-c

此时 lines 是:

[
    "----c----",
    "--c-b-c--",
    "c-b-a-b-c"
]

然后下半部分是:

lines[-2::-1]

结果是:

[
    "--c-b-c--",
    "----c----"
]

最后合起来:

----c----
--c-b-c--
c-b-a-b-c
--c-b-c--
----c----

7. 方法总结

这一类图形题,不要一开始就写代码。先按这个顺序思考:

第一,找最大宽度。
因为图案通常需要对齐,必须先知道每一行应该占多宽。

第二,找“核心行内容”。
这里核心不是横杠,而是字母变化:

c
c-b-c
c-b-a-b-c

第三,判断是否有对称性。
这道题上下对称,所以只需要生成上半部分和中间行。

第四,把每一行作为字符串放进列表。
最后用:

"\n".join(lines)

一次性合成最终结果。

第五,图形题常用工具包括:

join()
center()
append()
range()

这道题最关键的是:

先生成每行字母,再用 - 连接,再居中补 -,最后上下镜像。

8. 同类型小练习

请你尝试打印下面这种数字菱形。

输入:

3

输出:

--3--
-323-
32123
-323-
--3--

提示:

这道题和 alphabet rangoli 很像,只是把字母换成了数字。

思考方向:

size = 3
第一行:3
第二行:3 2 3
第三行:3 2 1 2 3

仍然可以先生成上半部分,再镜像下半部分。

bottom_lines = lines[-2::-1]
    bottom_lines = lines[-2::-1],这个代码没有看懂

这行代码:

bottom_lines = lines[-2::-1]

核心是在做一件事:lines 里取出下半部分,并且倒着取,避免重复中间最长的一行。

可以先把它拆成三部分看:

lines[start : stop : step]

对应到你的代码就是:

lines[-2 :  : -1]

含义是:

部分写法含义
start-2从倒数第二个元素开始
stop空着一直取到列表开头
step-1倒着取

所以:

lines[-2::-1]

意思就是:

从倒数第二行开始,倒着一路取到第一行。


用你的 rangoli 举例。假设 size = 3,前面生成出来的 lines 是:

lines = [
    "----c----",
    "--c-b-c--",
    "c-b-a-b-c"
]

这三行其实是:

----c----
--c-b-c--
c-b-a-b-c

这只是上半部分加中间行。

完整图案还需要下半部分:

--c-b-c--
----c----

这个下半部分正好就是把前面的上半部分倒过来。

但是注意,不能从最后一行开始倒着取。

如果写成:

lines[-1::-1]

结果是:

[
    "c-b-a-b-c",
    "--c-b-c--",
    "----c----"
]

这样会把中间最长的一行 "c-b-a-b-c" 重复一次。

所以要从倒数第二行开始:

lines[-2::-1]

结果是:

[
    "--c-b-c--",
    "----c----"
]

正好就是下半部分。


所以这两行:

bottom_lines = lines[-2::-1]
all_lines = lines + bottom_lines

等价于:

bottom_lines = [
    "--c-b-c--",
    "----c----"
]

all_lines = [
    "----c----",
    "--c-b-c--",
    "c-b-a-b-c",
    "--c-b-c--",
    "----c----"
]

最后再:

print("\n".join(all_lines))

输出完整图案。


对初学者来说,这行代码可以先写成更容易理解的循环版本:

bottom_lines = []

for i in range(len(lines) - 2, -1, -1):
    bottom_lines.append(lines[i])

这段和下面这一行效果一样:

bottom_lines = lines[-2::-1]

逐步解释:

range(len(lines) - 2, -1, -1)

表示:

从倒数第二行的下标开始,倒着走,一直走到第 0 行。


这一句的关键记法是:

列表[-2::-1]

可以理解成:

不要最后一个,从倒数第二个开始,倒着取到开头。

在这道题里,最后一个就是中间最长的那一行。它已经在 lines 里面出现过了,所以生成下半部分时不能再重复它。

小练习:

nums = [10, 20, 30, 40, 50]
print(nums[-2::-1])

你可以先不要运行,自己判断它会输出什么。提示:从倒数第二个元素 40 开始,倒着取到开头。

文末附加内容
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇