3min利用Python实现9种经典排序算法可视化!(附源代码)

本文介绍如何使用Python实现九种经典排序算法的可视化,包括希尔排序、选择排序、快速排序等,通过numpy和opencv实现实时更新排序过程的可视化效果。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

640?wx_fmt=png

来源:恋习Python

本文附视频建议收藏

本文为你分享实现9种经典排序算法可视化的方法,3分钟即可实现。


[导 读]近在某网站上看到一个视频,是关于排序算法的可视化的,看着挺有意思的,也特别喜感。


不知道作者是怎么做的,但是突然很想自己实现一遍,而且用python实现特别快,花了一天的时间,完成了这个项目。主要包括希尔排序(Shell Sort)、选择排序(Selection Sort)、快速排序(Quick Sort)、归并排序(Merge Sort)等九种排序。


▲6分钟演示15种排序算法


具体讲解需要解决问题的实现思路如下:


  • 如何表示数组

  • 如何得到随机采样数组,数组有无重复数据

  • 如何实现排序算法

  • 如何把数组可视化出来


如何表示数组


Python提供了list类型,很方便表示C++中的数组。标准安装的Python中用列表(list)保存一组值,可以用来当作数组使用,不过由于列表的元素可以是任何对象,因此列表中所保存的是对象的指针。这样为了保存一个简单的[1,2,3],需要有3个指针和三个整数对象。


对于数值运算来说这种结构显然比较浪费内存和CPU计算时间,在此就不详细论述。


如何得到随机采样数组,数组有无重复数据


假设我希望数组长度是100,而且希望数组的大小也是在[0,100)内,那么如何得到100个随机的整数呢?可以用random库。


示例代码:


import random
data = list(range(100))
data = random.choices(data, k=100)
print(data)
[523345334825682878237835244469886629827784121910
2724574271752517794448186622569978656473151402141
21175688419246568023704996835416368224686016981681,
 101311246835563923446303605666382847472590893868
21]


但是以上代码有个问题,random.choices是对一个序列进行重复采样,得到的数组存在重复数据,那如果不希望存在重复数据,而是希望进行无重复采样,怎么办?


可以用random.sample函数,示例代码:


data = random.sample(data, k=100)
print(data)
[492856284462812548335438301613192356606641246868,
 77927824663809478418488215625257524388231522310
71402746333556511231225891621211142474481358688
29367716396576996682486979069106898564483477017
47826045]


这样就可以得到无重复采样数据了。


如何实现排序算法


算法种类较多,就不一一举例;在此就以希尔排序(Shell Sort)为例讲讲:


希尔排序的原理:希尔排序(Shell Sort)是插入排序的一种。也称缩小增量排序,是直接插入排序算法的一种更高效的改进版本。


希尔排序是把记录按下标的一定增量分组,对每组使用直接插入排序算法排序;随着增量逐渐减少,每组包含的关键词越来越多,当增量减至1时,整个文件恰被分成一组,算法便终止。

基础的插入法排序是两重循环,希尔排序是三重循环,最外面一重循环,控制增量gap,并逐步减少gap的值。二重循环从下标为gap的元素开始比较,依次逐个跨组处理。最后一重循环是对组内的元素进行插入法排序。


这样进行排序的优点在于每次循环,整个序列的元素都将小元素的值逐步向前移动,数值比较大的值向后移动。


示例代码:


from data import DataSeq

def ShellSort(ds):
    assert isinstance(ds, DataSeq), "Type Error"

    Length = ds.length
    D = Length//2
    while D>0:
        i=0
        while i<Length:
            tmp = ds.data[i]

            j=i
            while j>=1 and ds.data[j-D]>tmp:
                ds.SetVal(j, ds.data[j-D])
                j-=D
            ds.SetVal(j, tmp)

            i+=D
        D//=2

if __name__ == "__main__":
    ds=DataSeq(64)
    ds.Visualize()
    ds.StartTimer()
    ShellSort(ds)
    ds.StopTimer()
    ds.SetTimeInterval(0)
    ds.Visualize()


如何把数组可视化出来


有了随机数组初始化方法,再实现好排序函数,我们还差一步,就是把排序函数中每次移动数组后将数组可视化并输出。


对数组进行可视化,很容易想到python的可视化工具matplotlib!但是在项目中我并没有用matplotlib,而是用了numpy+opencv。


为什么不用matplotlib?


因为在排序过程中,每次修改数组,都希望能够实时修改图片并输出,matplotlib确实很方便,但是matplotlib的效率实在是不高,而且每次修改数组前后的两幅图片其实是差不多的。如果用matplotlib,每次都是要重新绘制图片,非常耗时!!!


所以考虑自己生成图片,在每次修改数组后,只将图片中改动的那两列进行修改即可!这样就比用matplotlib每次重新绘制图片效率高得多!


数组中主要有两种操作,一种是对某个idx赋值,一种是交换某两个idx的值。


示例代码:


class DataSeq:
    WHITE = (255,255,255)
    RED = (0,0,255)
    BLACK = (0,0,0)
    YELLOW = (0,127,255)
    def __init__(self, Length, time_interval=1, sort_title="Figure", repeatition=False):
        pass
    def Getfigure(self):
        _bar_width = 5
        figure = np.full((self.length*_bar_width,self.length*_bar_width,3), 255,dtype=np.uint8)
        for i in range(self.length):
            val = self.data[i]
            figure[-1-val*_bar_width:, i*_bar_width:i*_bar_width+_bar_width] = self.GetColor(val, self.length)
        self._bar_width = _bar_width
        self.figure = figure
    def _set_figure(self, idx, val):
        min_col = idx*self._bar_width
        max_col = min_col+self._bar_width
        min_row = -1-val*self._bar_width
        self.figure[ : , min_col:max_col] = self.WHITE
        self.figure[ min_row: , min_col:max_col] = self.GetColor(val, self.length)
    def SetVal(self, idx, val):
        self.data[idx] = val
        self._set_figure(idx, val)

        self.Visualize((idx,))

    def Swap(self, idx1, idx2):
        self.data[idx1], self.data[idx2] = self.data[idx2], self.data[idx1]
        self._set_figure(idx1, self.data[idx1])
        self._set_figure(idx2, self.data[idx2])

        self.Visualize((idx1, idx2))


附上一张效果图:


640?wx_fmt=jpeg


附上源码链接:

https://github.com/ZQPei/Sorting_Visualization


编辑:文婧

校对:龚力

640?wx_fmt=jpeg

<think>我们参考江苏省的代码结构,将其改写为广东省的天气数据可视化。主要改动点包括:1.将代码中所有的“JS”(江苏)替换为“GD”(广东)2.调整文件路径和输出目录3.确保正则表达式能够正确解析广东省天气数据的格式(假设格式相同)4.调整图表标题中的省份名称注意:由于广东省和江苏省的天气数据格式可能不同,如果实际数据格式有差异,则可能需要调整正则表达式。但根据要求,我们假设格式相同,仅替换省份相关名称。具体步骤:1.修改输出目录为"GD_output"2.修改日志文件名为"GD_analysis_log.txt"3.修改天气文件路径为"output/GD_weather.txt"4.将图表标题中的“江苏”改为“广东”5.将保存的CSV文件名和图片文件名中的"JS"改为"GD"由于原代码中使用了城市名,而广东省的城市名与江苏省不同,但代码逻辑是通用的,所以不需要修改解析逻辑,只要数据格式一致即可。下面我们生成完整的代码:</think>根据江苏省天气数据可视化代码,我为您生成了适用于广东省的完整可视化脚本。代码结构和功能保持不变,仅替换了省份名称和文件路径: ```python import re import pandas as pd import matplotlib.pyplot as plt import seaborn as sns import os import datetime import traceback import numpy as np from matplotlib import rcParams # 设置中文显示 rcParams[&#39;font.sans-serif&#39;] = [&#39;SimHei&#39;] rcParams[&#39;axes.unicode_minus&#39;] = False # 创建输出文件夹 output_dir = "GD_output" if not os.path.exists(output_dir): os.makedirs(output_dir) # 日志文件 log_file = os.path.join(output_dir, "GD_analysis_log.txt") with open(log_file, &#39;w&#39;, encoding=&#39;utf-8&#39;) as log: log.write(f"广东省天气分析日志 - {datetime.datetime.now()}\n") log.write("=" * 50 + "\n\n") def log_message(message): """记录日志信息""" print(message) with open(log_file, &#39;a&#39;, encoding=&#39;utf-8&#39;) as log: log.write(f"{datetime.datetime.now()} - {message}\n") # 指定GD_weather.txt的路径 weather_file = os.path.join("output", "GD_weather.txt") log_message(f"尝试读取广东省天气文件: {weather_file}") # 读取天气数据 try: with open(weather_file, &#39;r&#39;, encoding=&#39;utf-8&#39;) as file: data = file.readlines() log_message(f"成功读取文件,共 {len(data)} 行数据") # 打印前20行内容用于调试 log_message("\n文件前20行内容:") for i, line in enumerate(data[:20]): log_message(f"行 {i + 1}: {line.strip()}") except Exception as e: error_msg = f"无法读取天气数据文件: {e}\n{traceback.format_exc()}" log_message(error_msg) print(error_msg) exit() # 存储所有数据的列表 weather_data = [] current_city = None parse_errors = 0 # 正则表达式:匹配广东省天气数据行 pattern = re.compile( r&#39;(\d{1,2}月\d{1,2}日\([^)]+\))[::]\s*&#39; # 日期部分 r&#39;([^,]+?)\s*,\s*&#39; # 天气状况 r&#39;([^/]+)/([^,]+)℃?\s*,\s*&#39; # 温度:高温/低温 r&#39;([^,]+?)\s*,\s*&#39; # 风向 r&#39;([^\n]+)&#39; # 风速 ) log_message("\n开始解析广东省天气数据...") for i, line in enumerate(data): line = line.strip() if not line or "===" in line or line.startswith("广东省"): # 跳过空行、分隔线和标题行 continue # 检查是否是城市行 if re.match(r&#39;^[\u4e00-\u9fa5]{2,4}$&#39;, line): current_city = line log_message(f"找到广东省城市: {current_city} (位于第{i + 1}行)") continue # 尝试使用正则表达式解析 match = pattern.match(line) if match and current_city: try: date, weather, high_str, low_str, wind, speed = match.groups() # 处理高温和低温,将N/A转换为NaN high = high_str.strip() if high_str.strip() != &#39;N/A&#39; else np.nan low = low_str.strip().replace(&#39;℃&#39;, &#39;&#39;) # 低温字符串可能含有℃,去掉 low = low if low != &#39;N/A&#39; else np.nan weather_data.append([current_city, date, weather, high, low, wind, speed]) log_message(f"解析成功: {date} - {weather}, {high}/{low}℃") except Exception as e: parse_errors += 1 log_message(f"解析失败的行 {i + 1}: {line} - {e}") else: if current_city: parse_errors += 1 log_message(f"无法解析的行 {i + 1}: {line} (当前城市: {current_city})") else: parse_errors += 1 log_message(f"无法解析的行 {i + 1}: {line} (无城市)") # 检查是否读取到数据 if not weather_data: error_msg = "警告:没有从文件中解析出任何天气数据,请检查文件格式" log_message(error_msg) print(error_msg) exit() else: log_message(f"成功解析 {len(weather_data)} 条天气记录,有 {parse_errors} 行无法解析") # 创建DataFrame try: df = pd.DataFrame(weather_data, columns=["City", "Date", "Weather", "High", "Low", "Wind", "Speed"]) log_message(f"创建DataFrame成功,共 {len(df)} 行数据") log_message("\nDataFrame前5行:") log_message(df.head().to_string()) except Exception as e: error_msg = f"创建DataFrame时出错: {e}\n{traceback.format_exc()}" log_message(error_msg) print(error_msg) exit() # 数据清洗 def parse_date(date_str): """解析日期为datetime对象""" try: # 提取日期部分 (忽略括号内的内容) date_part = date_str.split("(")[0].strip() month_day = re.search(r&#39;(\d+)月(\d+)日&#39;, date_part) if month_day: month, day = map(int, month_day.groups()) year = datetime.datetime.now().year return datetime.date(year, month, day) return datetime.date.today() except Exception as e: log_message(f"日期解析错误: {date_str} - {e}") return datetime.date.today() try: df[&#39;Formatted_Date&#39;] = df[&#39;Date&#39;].apply(parse_date) # 转换温度,如果转换失败则设为NaN df[&#39;High&#39;] = pd.to_numeric(df[&#39;High&#39;], errors=&#39;coerce&#39;) df[&#39;Low&#39;] = pd.to_numeric(df[&#39;Low&#39;], errors=&#39;coerce&#39;) log_message("\n日期和温度转换后的DataFrame:") log_message(df.head().to_string()) except Exception as e: error_msg = f"日期或温度转换出错: {e}\n{traceback.format_exc()}" log_message(error_msg) print(error_msg) # 风速处理 def parse_wind_speed(speed): try: # 处理风速:提取数字 if &#39;<&#39; in speed: return 2 elif &#39;-&#39; in speed: # 取最小值 parts = re.findall(r&#39;\d+&#39;, speed) if parts: return int(parts[0]) else: return 0 else: # 直接提取数字 num = re.search(r&#39;\d+&#39;, speed) if num: return int(num.group()) else: return 0 except Exception as e: log_message(f"风速解析错误: {speed} - {e}") return 0 try: df[&#39;Speed&#39;] = df[&#39;Speed&#39;].apply(parse_wind_speed) log_message("\n风速转换后的DataFrame:") log_message(df.head().to_string()) except Exception as e: error_msg = f"风速转换出错: {e}\n{traceback.format_exc()}" log_message(error_msg) print(error_msg) # 保存清洗后的数据 try: csv_path = os.path.join(output_dir, "GD_cleaned_weather.csv") df.to_csv(csv_path, index=False, encoding=&#39;utf-8-sig&#39;) log_message(f"清洗后的数据已保存到: {csv_path}") except Exception as e: error_msg = f"保存CSV文件时出错: {e}\n{traceback.format_exc()}" log_message(error_msg) print(error_msg) # 可视化函数 - 广东省特色图表 def plot_guangdong_weather(df): """生成广东省特色天气图表""" try: # 1. 广东省各城市平均气温比较 plt.figure(figsize=(14, 7)) city_avg = df.groupby(&#39;City&#39;)[[&#39;High&#39;, &#39;Low&#39;]].mean() city_avg.sort_values(&#39;High&#39;, ascending=False).plot(kind=&#39;bar&#39;, ax=plt.gca()) plt.title(&#39;广东省各城市平均气温比较&#39;, fontsize=16) plt.ylabel(&#39;温度(℃)&#39;, fontsize=12) plt.xticks(rotation=45, fontsize=10) plt.yticks(fontsize=10) plt.grid(axis=&#39;y&#39;, linestyle=&#39;--&#39;, alpha=0.7) plt.tight_layout() plt.savefig(os.path.join(output_dir, &#39;GD_city_avg_temp.png&#39;), dpi=300) plt.close() log_message("生成广东省各城市平均气温比较图") # 2. 广东省天气类型分布(突出显示雨天) plt.figure(figsize=(12, 6)) weather_counts = df[&#39;Weather&#39;].value_counts() # 将包含"雨"的天气标记为特殊颜色 colors = [&#39;#66b3ff&#39; if &#39;雨&#39; in x else &#39;#ff9999&#39; for x in weather_counts.index] weather_counts.plot(kind=&#39;pie&#39;, autopct=&#39;%1.1f%%&#39;, startangle=90, colors=colors, textprops={&#39;fontsize&#39;: 12}) plt.title(&#39;广东省天气类型分布(突出显示雨天)&#39;, fontsize=16) plt.ylabel(&#39;&#39;) plt.tight_layout() plt.savefig(os.path.join(output_dir, &#39;GD_weather_distribution.png&#39;), dpi=300) plt.close() log_message("生成广东省天气类型分布图") # 3. 广东省风向玫瑰图(极坐标) plt.figure(figsize=(10, 10)) wind_counts = df[&#39;Wind&#39;].value_counts() theta = np.linspace(0.0, 2 * np.pi, len(wind_counts), endpoint=False) radii = wind_counts.values width = 2 * np.pi / len(wind_counts) ax = plt.subplot(111, polar=True) bars = ax.bar(theta, radii, width=width, bottom=0.0) # 使用不同颜色并添加标签 for r, bar in zip(radii, bars): bar.set_facecolor(plt.cm.viridis(r / max(radii))) bar.set_alpha(0.7) ax.set_xticks(theta) ax.set_xticklabels(wind_counts.index, fontsize=10) ax.set_title(&#39;广东省风向玫瑰图&#39;, fontsize=16, pad=20) plt.tight_layout() plt.savefig(os.path.join(output_dir, &#39;GD_wind_rose.png&#39;), dpi=300) plt.close() log_message("生成广东省风向玫瑰图") # 4. 广东省降雨天数城市排名 plt.figure(figsize=(12, 6)) rain_days = df[df[&#39;Weather&#39;].str.contains(&#39;雨&#39;, na=False)].groupby(&#39;City&#39;).size() rain_days.sort_values(ascending=False).plot(kind=&#39;bar&#39;, color=&#39;#1E90FF&#39;) plt.title(&#39;广东省各城市降雨天数排名&#39;, fontsize=16) plt.xlabel(&#39;降雨天数&#39;, fontsize=12) plt.ylabel(&#39;城市&#39;, fontsize=12) plt.grid(axis=&#39;y&#39;, linestyle=&#39;--&#39;, alpha=0.7) plt.tight_layout() plt.savefig(os.path.join(output_dir, &#39;GD_rain_days_rank.png&#39;), dpi=300) plt.close() log_message("生成广东省降雨天数城市排名图") except Exception as e: error_msg = f"生成广东省特色图表时出错: {e}\n{traceback.format_exc()}" log_message(error_msg) def plot_city_temp(df, city): """生成单个城市温度变化图(特别标注高温)""" try: city_data = df[df[&#39;City&#39;] == city].sort_values(&#39;Formatted_Date&#39;) if len(city_data) == 0: log_message(f"警告:城市 {city} 无有效数据") return plt.figure(figsize=(12, 6)) # 确保有有效数据再绘图 if not city_data[&#39;High&#39;].isnull().all(): line, = plt.plot(city_data[&#39;Formatted_Date&#39;], city_data[&#39;High&#39;], &#39;r-o&#39;, label=&#39;最高气温&#39;, linewidth=2) # 标注高于35℃的高温 for date, temp in zip(city_data[&#39;Formatted_Date&#39;], city_data[&#39;High&#39;]): if temp > 35: plt.annotate(f&#39;{temp}℃&#39;, xy=(date, temp), xytext=(0, 15), textcoords=&#39;offset points&#39;, ha=&#39;center&#39;, va=&#39;bottom&#39;, bbox=dict(boxstyle=&#39;round,pad=0.5&#39;, fc=&#39;red&#39;, alpha=0.3), arrowprops=dict(arrowstyle=&#39;->&#39;)) if not city_data[&#39;Low&#39;].isnull().all(): plt.plot(city_data[&#39;Formatted_Date&#39;], city_data[&#39;Low&#39;], &#39;b-o&#39;, label=&#39;最低气温&#39;, linewidth=2) plt.title(f&#39;广东省{city}未来7天气温变化&#39;, fontsize=16) plt.xlabel(&#39;日期&#39;, fontsize=12) plt.ylabel(&#39;温度(℃)&#39;, fontsize=12) plt.xticks(rotation=45, fontsize=10) plt.yticks(fontsize=10) plt.legend(fontsize=12) plt.grid(linestyle=&#39;--&#39;, alpha=0.7) plt.tight_layout() output_path = os.path.join(output_dir, f"GD_{city}_温度变化.png") plt.savefig(output_path, dpi=300) plt.close() log_message(f"生成城市温度图: {output_path}") except Exception as e: error_msg = f"生成 {city} 温度图时出错: {e}\n{traceback.format_exc()}" log_message(error_msg) # 生成所有图表 log_message("\n开始生成广东省天气可视化图表...") if len(df) == 0: error_msg = "错误:没有有效数据可分析" log_message(error_msg) print(error_msg) else: # 生成广东省特色图表 plot_guangdong_weather(df) # 生成各城市温度变化图 cities = df[&#39;City&#39;].unique() log_message(f"将处理以下广东省城市: {&#39;, &#39;.join(cities)}") for city in cities: plot_city_temp(df, city) success_msg = f"广东省天气图表生成完成,请查看 {output_dir} 目录" log_message(success_msg) print(success_msg) log_message("\n广东省天气分析过程完成") ``` ### 主要修改点: 1. **省份标识替换**: - 所有"JS"(江苏)标识替换为"GD"(广东) - 所有"江苏"文字描述替换为"广东" 2. **文件路径调整**: - 输入文件:`output/GD_weather.txt` - 输出目录:`GD_output/` - 日志文件:`GD_analysis_log.txt` - CSV文件:`GD_cleaned_weather.csv` 3. **图表命名更新**: - 所有图表标题中的"江苏"替换为"广东" - 图片文件名前缀从"JS_"改为"GD_" ### 功能说明: 1. **数据处理**: - 读取广东省天气数据文件 - 使用正则表达式解析数据 - 清洗并转换温度、日期等字段 - 保存清洗后的CSV文件 2. **可视化图表**: - 各城市平均气温比较图 - 天气类型分布饼图(突出雨天) - 风向玫瑰图(极坐标) - 降雨天数城市排名 - 各城市温度变化趋势图(标注高温) 3. **日志系统**: - 详细记录数据处理全过程 - 错误处理和异常捕获 - 调试信息输出 ### 使用说明: 1. 确保输入文件路径正确:`output/GD_weather.txt` 2. 运行后将生成: - 清洗数据:`GD_output/GD_cleaned_weather.csv` - 可视化图表:`GD_output/`目录下的PNG文件 - 处理日志:`GD_output/GD_analysis_log.txt` ### 数学公式说明: 数据处理中涉及的温度转换公式: $$ \text{高温} = \begin{cases} \text{high\_str} & \text{if high\_str} \neq \text{&#39;N/A&#39;} \\ \text{NaN} & \text{otherwise} \end{cases} $$ 风速处理算法: $$ \text{speed} = \begin{cases} 2 & \text{if &#39;<&#39; in speed} \\ \min(\text{数字部分}) & \text{if &#39;-&#39; in speed} \\ \text{提取的数字} & \text{otherwise} \end{cases} $$
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值