diff --git a/lib/components/toly_ui/popable/drop_selectable_widget.dart b/lib/components/toly_ui/popable/drop_selectable_widget.dart new file mode 100644 index 0000000..da8e4e8 --- /dev/null +++ b/lib/components/toly_ui/popable/drop_selectable_widget.dart @@ -0,0 +1,199 @@ +import 'dart:math'; + +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; + +typedef OnDropSelected = void Function(int index); + +class DropSelectableWidget extends StatefulWidget { + final List data; + final OnDropSelected? onDropSelected; + final Color disableColor; + final double iconSize; + final double height; + final double width; + final double fontSize; + + const DropSelectableWidget( + {Key? key, + this.data = const [], + this.onDropSelected, + this.disableColor = Colors.black, + this.iconSize = 24, + this.height = 30, + this.width = 200, + this.fontSize = 14, + }) + : super(key: key); + + @override + _DropSelectableWidgetState createState() => _DropSelectableWidgetState(); +} + +class _DropSelectableWidgetState extends State + with SingleTickerProviderStateMixin { + late FocusNode _node; + bool _focused = false; + late FocusAttachment _nodeAttachment; + OverlayEntry? _overlayEntry; + late AnimationController _ctrl; + late Animation animation; + final LayerLink layerLink = LayerLink(); + + int _selectedIndex = 0; + + @override + void initState() { + super.initState(); + + _ctrl = AnimationController( + vsync: this, + duration: const Duration(milliseconds: 200), + ); + + animation = Tween(begin: 0, end: pi).animate(_ctrl); + _node = FocusNode() + ..addListener(() { + if (_node.hasFocus != _focused) { + if (!_focused) { + _ctrl.forward(); + _showOverlay(); + } else { + _hideOverlay(); + _ctrl.reverse(); + } + setState(() { + _focused = _node.hasFocus; + }); + } + }); + _nodeAttachment = _node.attach(context); + } + + @override + void dispose() { + _node.dispose(); + _ctrl.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + _nodeAttachment.reparent(); + return GestureDetector( + behavior: HitTestBehavior.opaque, + onTap: () { + if (_focused) { + _node.unfocus(); + } else { + _node.requestFocus(); + } + }, + child: CompositedTransformTarget( + link: layerLink, + child: buildTarget(), + ), + ); + } + + void _showOverlay() { + _overlayEntry = _createOverlayEntry(); + Overlay.of(context)?.insert(_overlayEntry!); + } + + void _hideOverlay() { + _overlayEntry?.remove(); + } + + Widget buildTarget() { + return Container( + width: widget.width, + height: widget.height, + padding: const EdgeInsets.only(left: 10, right: 10), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(20), + border: Border.all( + width: _focused ?1:1/View.of(context).devicePixelRatio, + color: _focused ? Colors.blue : widget.disableColor, + )), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + widget.data.isNotEmpty ? widget.data[_selectedIndex] : "暂无数据",style: TextStyle( + height: 1, + fontSize: widget.fontSize + ),), + AnimatedBuilder( + animation: animation, + builder: (_, child) => Transform.rotate( + angle: animation.value, + child: child, + ), + child: Icon( + Icons.keyboard_arrow_down, + size: widget.iconSize, + ), + ), + ], + ), + ); + } + + OverlayEntry _createOverlayEntry() => OverlayEntry( + builder: (BuildContext context) => UnconstrainedBox( + child: CompositedTransformFollower( + link: layerLink, + targetAnchor: Alignment.bottomCenter, + followerAnchor: Alignment.topCenter, + child: Padding( + padding: const EdgeInsets.only(top: 4.0), + child: Material( + shape: const RoundedRectangleBorder( + side: BorderSide.none, + borderRadius: BorderRadius.all(Radius.circular(5))), + elevation: 1, + child: ClipRRect( + borderRadius: BorderRadius.circular(5), + child: Container( + height: 200, + // alignment: Alignment.center, + decoration: const BoxDecoration( + color: Color(0xffDAE3FF), + ), + // padding: const EdgeInsets.only(top: 5), + width: widget.width, + child: CupertinoScrollbar( + child: ListView.builder( + padding: EdgeInsets.zero, + // shrinkWrap: true, + itemCount: widget.data.length, + itemBuilder: _buildItem), + ), + ), + ), + ), + ), + ), + ), + ); + + Widget _buildItem(BuildContext context, int index) { + return Material( + child: InkWell( + onTap: () { + if (_selectedIndex != index) widget.onDropSelected?.call(index); + _selectedIndex = index; + _overlayEntry?.markNeedsBuild(); + _node.unfocus(); + }, + child: Container( + padding: const EdgeInsets.all(8), + color: index == _selectedIndex + ? Colors.blue.withOpacity(0.2) + : Colors.transparent, + child: Text(widget.data[index],style: TextStyle(fontSize: widget.fontSize),)), + ), + ); + } +} \ No newline at end of file diff --git a/lib/components/toly_ui/toly_ui.dart b/lib/components/toly_ui/toly_ui.dart index 5d49a81..9d9a7f6 100644 --- a/lib/components/toly_ui/toly_ui.dart +++ b/lib/components/toly_ui/toly_ui.dart @@ -1,4 +1,5 @@ export 'navigation/menu_meta.dart'; export 'navigation/toly_breadcrumb.dart'; -export 'navigation/toly_navigation_rail.dart'; \ No newline at end of file +export 'navigation/toly_navigation_rail.dart'; +export 'popable/drop_selectable_widget.dart'; \ No newline at end of file diff --git a/lib/v4/pages/sort/sort_page.dart b/lib/v4/pages/sort/sort_page.dart index 8172cc0..d440071 100644 --- a/lib/v4/pages/sort/sort_page.dart +++ b/lib/v4/pages/sort/sort_page.dart @@ -328,7 +328,6 @@ class _SortPageState extends State { // 从最后一个非叶子节点开始,构建最大堆 for (int i = numbers.length ~/ 2; i >= 0; i--) { await heapify(numbers, numbers.length, i); - streamController.add(numbers); } // 依次取出最大堆的根节点(最大值),并进行堆化 @@ -361,6 +360,7 @@ class _SortPageState extends State { } await Future.delayed(getDuration()); // 延迟操作,用于可视化排序过程 + streamController.add(numbers); } ///插入排序 @@ -448,7 +448,7 @@ class _SortPageState extends State { ///快速排序 quickSort(int leftIndex, int rightIndex) async { // 定义一个名为 _partition 的异步函数,用于划分数组,并返回划分后的基准元素的索引位置 - Future _partition(int left, int right) async { + Future _partition(int left, int right) async{ // 选择中间位置的元素作为基准元素 int p = (left + (right - left) / 2).toInt(); diff --git a/lib/v5/app/navigation/router/app_router_delegate.dart b/lib/v5/app/navigation/router/app_router_delegate.dart index 3e51873..6910126 100644 --- a/lib/v5/app/navigation/router/app_router_delegate.dart +++ b/lib/v5/app/navigation/router/app_router_delegate.dart @@ -7,14 +7,16 @@ import '../../../pages/color/color_detail_page.dart'; import '../../../pages/color/color_page.dart'; import '../../../pages/empty/empty_page.dart'; import '../../../pages/settings/settings_page.dart'; +import '../../../pages/user/user_page.dart'; import '../../../pages/counter/counter_page.dart'; -import '../../../pages/sort/sort_page.dart'; +import '../../../pages/sort/views/sort_page.dart'; import '../transition/fade_transition_page.dart'; import '../../../pages/color/color_add_page.dart'; const List kDestinationsPaths = [ '/color', '/counter', + '/sort', '/user', '/settings', ]; @@ -29,8 +31,9 @@ class AppRouterDelegate extends RouterDelegate with ChangeNotifier { int? get activeIndex { if(path.startsWith('/color')) return 0; if(path.startsWith('/counter')) return 1; - if(path.startsWith('/user')) return 2; - if(path.startsWith('/settings')) return 3; + if(path.startsWith('/sort')) return 2; + if(path.startsWith('/user')) return 3; + if(path.startsWith('/settings')) return 4; return null; } @@ -95,9 +98,12 @@ class AppRouterDelegate extends RouterDelegate with ChangeNotifier { child = const CounterPage(); } if (path == kDestinationsPaths[2]) { - child = const SortPage(); + child = SortPage(); } if (path == kDestinationsPaths[3]) { + child = const UserPage(); + } + if (path == kDestinationsPaths[4]) { child = const SettingPage(); } return [ diff --git a/lib/v5/app/navigation/views/app_navigation.dart b/lib/v5/app/navigation/views/app_navigation.dart index adcab6c..e1d002d 100644 --- a/lib/v5/app/navigation/views/app_navigation.dart +++ b/lib/v5/app/navigation/views/app_navigation.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import '../../../pages/sort/sort_setting.dart'; import '../router/app_router_delegate.dart'; import 'app_navigation_rail.dart'; import 'app_top_bar.dart'; @@ -10,6 +11,9 @@ class AppNavigation extends StatelessWidget { Widget build(BuildContext context) { double px1 = 1/View.of(context).devicePixelRatio; return Scaffold( + endDrawer: Drawer( + child: SortSettings(), + ), body: Row( children: [ const AppNavigationRail(), diff --git a/lib/v5/app/navigation/views/app_navigation_rail.dart b/lib/v5/app/navigation/views/app_navigation_rail.dart index 1ab9714..38aa912 100644 --- a/lib/v5/app/navigation/views/app_navigation_rail.dart +++ b/lib/v5/app/navigation/views/app_navigation_rail.dart @@ -14,6 +14,7 @@ class _AppNavigationRailState extends State { final List deskNavBarMenus = const [ MenuMeta(label: '颜色板', icon: Icons.color_lens_outlined), MenuMeta(label: '计数器', icon: Icons.add_chart), + MenuMeta(label: '排序', icon: Icons.sort), MenuMeta(label: '我的', icon: Icons.person), MenuMeta(label: '设置', icon: Icons.settings), ]; diff --git a/lib/v5/app/navigation/views/app_router_editor.dart b/lib/v5/app/navigation/views/app_router_editor.dart index 10d5701..9173948 100644 --- a/lib/v5/app/navigation/views/app_router_editor.dart +++ b/lib/v5/app/navigation/views/app_router_editor.dart @@ -1,6 +1,10 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:iroute/components/toly_ui/button/hover_icon_button.dart'; +import 'package:iroute/components/toly_ui/popable/drop_selectable_widget.dart'; +import '../../../pages/sort/functions.dart'; +import '../../../pages/sort/views/sort_bar.dart'; +import '../../../pages/sort/views/sort_button.dart'; import '../router/app_router_delegate.dart'; class AppRouterEditor extends StatefulWidget { @@ -25,6 +29,9 @@ class _AppRouterEditorState extends State { void _onRouteChange() { _controller.text=router.path; + setState(() { + + }); } @override @@ -36,29 +43,37 @@ class _AppRouterEditorState extends State { @override Widget build(BuildContext context) { - return Stack( - alignment: Alignment.centerRight, - children: [ - SizedBox( - child: CupertinoTextField( - controller: _controller, - style: TextStyle(fontSize: 14), - padding: EdgeInsets.only(left:12,top: 6,bottom: 6,right: 32), - placeholder: '输入路由地址导航', - onSubmitted: widget.onSubmit, - decoration: BoxDecoration(color: Color(0xffF1F2F3),borderRadius: BorderRadius.circular(6)), + print(router.path); + if(router.path=='/sort'){ + return SortBar(); + } + + return SizedBox( + width: 250, + child: Stack( + alignment: Alignment.centerRight, + children: [ + SizedBox( + child: CupertinoTextField( + controller: _controller, + style: TextStyle(fontSize: 14), + padding: EdgeInsets.only(left:12,top: 6,bottom: 6,right: 32), + placeholder: '输入路由地址导航', + onSubmitted: widget.onSubmit, + decoration: BoxDecoration(color: Color(0xffF1F2F3),borderRadius: BorderRadius.circular(6)), + ), ), - ), - Padding( - padding: const EdgeInsets.only(right: 8.0), - child: HoverIconButton( - icon: Icons.directions_outlined, - defaultColor: Color(0xff68696B), - onPressed:()=>widget.onSubmit?.call(_controller.text), - size: 20 - ), - ) - ], + Padding( + padding: const EdgeInsets.only(right: 8.0), + child: HoverIconButton( + icon: Icons.directions_outlined, + defaultColor: Color(0xff68696B), + onPressed:()=>widget.onSubmit?.call(_controller.text), + size: 20 + ), + ) + ], + ), ); } } diff --git a/lib/v5/app/navigation/views/app_top_bar.dart b/lib/v5/app/navigation/views/app_top_bar.dart index 1b95110..84890eb 100644 --- a/lib/v5/app/navigation/views/app_top_bar.dart +++ b/lib/v5/app/navigation/views/app_top_bar.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:iroute/components/components.dart'; +import '../../../pages/sort/functions.dart'; import '../router/app_router_delegate.dart'; import 'app_router_editor.dart'; @@ -20,7 +21,7 @@ class AppTopBar extends StatelessWidget { child: Row(children: [ const Spacer(), SizedBox( - width: 250, + // width: 200, child: AppRouterEditor( onSubmit: (path) => router.path = path, )), @@ -52,6 +53,7 @@ Map kRouteLabelMap = { '/color/detail': '颜色详情', '/counter': '计数器', '/user': '我的', + '/sort': '可视化排序', '/settings': '系统设置', }; diff --git a/lib/v5/app/unit_app.dart b/lib/v5/app/unit_app.dart index 60d28f8..36e67ab 100644 --- a/lib/v5/app/unit_app.dart +++ b/lib/v5/app/unit_app.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import '../pages/sort/provider/state.dart'; import 'navigation/router/app_router_delegate.dart'; import 'navigation/views/app_navigation.dart'; import 'navigation/views/app_navigation_rail.dart'; @@ -8,20 +9,23 @@ class UnitApp extends StatelessWidget { @override Widget build(BuildContext context) { - return MaterialApp( - theme: ThemeData( - fontFamily: "宋体", - scaffoldBackgroundColor: Colors.white, - appBarTheme: const AppBarTheme( - elevation: 0, - iconTheme: IconThemeData(color: Colors.black), - titleTextStyle: TextStyle( - color: Colors.black, - fontSize: 18, - fontWeight: FontWeight.bold, - ))), - debugShowCheckedModeBanner: false, - home: AppNavigation() + return SortStateScope( + notifier: SortState(), + child: MaterialApp( + theme: ThemeData( + fontFamily: "宋体", + scaffoldBackgroundColor: Colors.white, + appBarTheme: const AppBarTheme( + elevation: 0, + iconTheme: IconThemeData(color: Colors.black), + titleTextStyle: TextStyle( + color: Colors.black, + fontSize: 18, + fontWeight: FontWeight.bold, + ))), + debugShowCheckedModeBanner: false, + home: AppNavigation() + ), ); } } diff --git a/lib/v5/pages/sort/functions.dart b/lib/v5/pages/sort/functions.dart new file mode 100644 index 0000000..6ae1176 --- /dev/null +++ b/lib/v5/pages/sort/functions.dart @@ -0,0 +1,46 @@ +import 'functions/bubble.dart'; +import 'functions/cocktail.dart'; +import 'functions/comb.dart'; +import 'functions/cycle.dart'; +import 'functions/gnome.dart'; +import 'functions/heap.dart'; +import 'functions/insertion.dart'; +import 'functions/merage.dart'; +import 'functions/pigeonHole.dart'; +import 'functions/quick.dart'; +import 'functions/selection.dart'; +import 'functions/shell.dart'; + +typedef SortFunction = Future Function(List src, SortCallback callback); +typedef SortCallback = Future Function(List dist); + +Map sortFunctionMap = { + 'insertion': insertionSort, + 'bubble': bubbleSort, + 'cocktail': cocktailSort, + 'comb': combSort, + 'pigeonHole': pigeonHoleSort, + 'shell': shellSort, + 'selection': selectionSort, + 'gnome': gnomeSort, + 'cycle': cycleSort, + 'heap': heapSort, + 'quick': quickSort, + 'mergeSort': mergeSort, +}; + +Map sortNameMap = { + 'insertion': '插入排序', + 'bubble': '冒泡排序', + 'cocktail': '鸡尾酒排序(双向冒泡排序)', + 'comb': '梳排序', + 'pigeonHole': '鸽巢排序', + 'shell': '希尔排序', + 'selection': '选择排序', + 'gnome': '侏儒排序', + 'cycle': '循环排序', + 'heap': '堆排序', + 'quick': '快速排序', + 'mergeSort': '归并排序', +}; + diff --git a/lib/v5/pages/sort/functions/bubble.dart b/lib/v5/pages/sort/functions/bubble.dart new file mode 100644 index 0000000..3cf3c99 --- /dev/null +++ b/lib/v5/pages/sort/functions/bubble.dart @@ -0,0 +1,19 @@ +import '../functions.dart'; + +///冒泡排序 +Future bubbleSort(List src, SortCallback callback ) async{ + //控制需要进行排序的次数。每一轮循环都会确定一个数字的最终位置。 + for (int i = 0; i < src.length; ++i) { + //遍历当前未排序的元素,通过相邻的元素比较并交换位置来完成排序。 + for (int j = 0; j < src.length - i - 1; ++j) { + //如果 _numbers[j] 大于 _numbers[j + 1],则交换它们的位置,确保较大的元素移到右边。 + if (src[j] > src[j + 1]) { + int temp = src[j]; + src[j] = src[j + 1]; + src[j + 1] = temp; + } + //实现一个延迟,以便在ui上展示排序的动画效果 + await callback(src); + } + } +} \ No newline at end of file diff --git a/lib/v5/pages/sort/functions/cocktail.dart b/lib/v5/pages/sort/functions/cocktail.dart new file mode 100644 index 0000000..8c2d18c --- /dev/null +++ b/lib/v5/pages/sort/functions/cocktail.dart @@ -0,0 +1,52 @@ +import '../functions.dart'; + +///鸡尾酒排序(双向冒泡排序) +Future cocktailSort(List src, SortCallback callback ) async { + bool swapped = true; // 表示是否进行了交换 + int start = 0; // 当前未排序部分的起始位置 + int end = src.length; // 当前未排序部分的结束位置 + + // 开始排序循环,只有当没有进行交换时才会退出循环 + while (swapped == true) { + swapped = false; + + // 从左往右遍历需要排序的部分 + for (int i = start; i < end - 1; ++i) { + // 对每两个相邻元素进行比较 + if (src[i] > src[i + 1]) { + // 如果前面的元素大于后面的元素,则交换它们的位置 + int temp = src[i]; + src[i] = src[i + 1]; + src[i + 1] = temp; + swapped = true; // 进行了交换 + } + + // 实现动画效果,延迟一段时间后更新数组状态 + await callback(src); + } + + // 如果没有进行交换,则说明已经排好序,退出循环 + if (swapped == false) break; + // 重设为false,准备进行下一轮排序 + swapped = false; + // 将end设置为上一轮排序的最后一个元素的位置 + end = end - 1; + + // 从右往左遍历需要排序的部分 + for (int i = end - 1; i >= start; i--) { + // 对每两个相邻元素进行比较 + if (src[i] > src[i + 1]) { + // 如果前面的元素大于后面的元素,则交换它们的位置 + int temp = src[i]; + src[i] = src[i + 1]; + src[i + 1] = temp; + swapped = true; // 进行了交换 + } + + // 实现动画效果,延迟一段时间后更新数组状态 + await callback(src); + } + // 将start向右移一位,准备下一轮排序 + start = start + 1; + } +} \ No newline at end of file diff --git a/lib/v5/pages/sort/functions/comb.dart b/lib/v5/pages/sort/functions/comb.dart new file mode 100644 index 0000000..821f4a9 --- /dev/null +++ b/lib/v5/pages/sort/functions/comb.dart @@ -0,0 +1,34 @@ +import '../functions.dart'; + +///梳排序(Comb Sort) +Future combSort(List src, SortCallback callback) async{ + int gap = src.length; + + bool swapped = true; + + // 当间隔不为1或存在交换时执行循环 + while (gap != 1 || swapped == true) { + // 通过缩小间隔来逐步将元素归位 + gap = getNextGap(gap); + swapped = false; + for (int i = 0; i < src.length - gap; i++) { + // 如果当前元素大于间隔位置上的元素,则交换它们的位置 + if (src[i] > src[i + gap]) { + int temp = src[i]; + src[i] = src[i + gap]; + src[i + gap] = temp; + swapped = true; + } + + // 实现一个延迟,以便在 UI 上展示排序的动画效果。 + await callback(src); + } + } +} + +int getNextGap(int gap) { + // 根据当前间隔值计算下一个间隔值 + gap = (gap * 10) ~/ 13; + if (gap < 1) return 1; + return gap; +} \ No newline at end of file diff --git a/lib/v5/pages/sort/functions/cycle.dart b/lib/v5/pages/sort/functions/cycle.dart new file mode 100644 index 0000000..4bef6eb --- /dev/null +++ b/lib/v5/pages/sort/functions/cycle.dart @@ -0,0 +1,54 @@ +import '../functions.dart'; + +///循环排序 +Future cycleSort(List src, SortCallback callback) async { + int writes = 0; + for (int cycleStart = 0; cycleStart <= src.length - 2; cycleStart++) { + int item = src[cycleStart]; + int pos = cycleStart; + + // 在未排序部分中寻找比当前元素小的元素个数 + for (int i = cycleStart + 1; i < src.length; i++) { + if (src[i] < item) pos++; + } + + // 如果当前元素已经在正确位置上,则跳过此次迭代 + if (pos == cycleStart) { + continue; + } + + // 将当前元素放置到正确的位置上,并记录写操作次数 + while (item == src[pos]) { + pos += 1; + } + if (pos != cycleStart) { + int temp = item; + item = src[pos]; + src[pos] = temp; + writes++; + } + + // 循环将位于当前位置的元素放置到正确的位置上 + while (pos != cycleStart) { + pos = cycleStart; + // 继续在未排序部分中寻找比当前元素小的元素个数 + for (int i = cycleStart + 1; i < src.length; i++) { + if (src[i] < item) pos += 1; + } + + // 将当前元素放置到正确的位置上,并记录写操作次数 + while (item == src[pos]) { + pos += 1; + } + if (item != src[pos]) { + int temp = item; + item = src[pos]; + src[pos] = temp; + writes++; + } + + // 添加延迟操作以展示排序过程 + await callback(src); + } + } +} \ No newline at end of file diff --git a/lib/v5/pages/sort/functions/gnome.dart b/lib/v5/pages/sort/functions/gnome.dart new file mode 100644 index 0000000..5e08fc3 --- /dev/null +++ b/lib/v5/pages/sort/functions/gnome.dart @@ -0,0 +1,22 @@ +import '../functions.dart'; + +///地精排序 (侏儒排序) +Future gnomeSort(List src, SortCallback callback) async { + int index = 0; + while (index < src.length) { + // 当 index 小于数组长度时执行循环 + if (index == 0) index++; + if (src[index] >= src[index - 1]) { + // 如果当前元素大于等于前面的元素,则将 index 加1 + index++; + } else { + // 否则,交换这两个元素,并将 index 减1(使得元素可以沉到正确位置) + int temp = src[index]; + src[index] = src[index - 1]; + src[index - 1] = temp; + index--; + } + await callback(src); + } + return; +} \ No newline at end of file diff --git a/lib/v5/pages/sort/functions/heap.dart b/lib/v5/pages/sort/functions/heap.dart new file mode 100644 index 0000000..9f5410b --- /dev/null +++ b/lib/v5/pages/sort/functions/heap.dart @@ -0,0 +1,38 @@ +import '../functions.dart'; + +///堆排序 +Future heapSort(List src, SortCallback callback) async { + // 从最后一个非叶子节点开始,构建最大堆 + for (int i = src.length ~/ 2; i >= 0; i--) { + await heapify(src,callback, src.length, i); + } + + // 依次取出最大堆的根节点(最大值),并进行堆化 + for (int i = src.length - 1; i >= 0; i--) { + int temp = src[0]; + src[0] = src[i]; + src[i] = temp; + await heapify(src, callback,i, 0); + } +} + +Future heapify(List src, SortCallback callback, int n, int i) async{ + int largest = i; + int l = 2 * i + 1; // 左子节点索引 + int r = 2 * i + 2; // 右子节点索引 + + // 如果左子节点存在并且大于父节点,则更新最大值索引 + if (l < n && src[l] > src[largest]) largest = l; + + // 如果右子节点存在并且大于父节点或左子节点,则更新最大值索引 + if (r < n && src[r] > src[largest]) largest = r; + + // 如果最大值索引不等于当前节点索引,则交换节点值,并递归进行堆化 + if (largest != i) { + int temp = src[i]; + src[i] = src[largest]; + src[largest] = temp; + heapify(src,callback, n, largest); + } + await callback(src); +} \ No newline at end of file diff --git a/lib/v5/pages/sort/functions/insertion.dart b/lib/v5/pages/sort/functions/insertion.dart new file mode 100644 index 0000000..b1c7814 --- /dev/null +++ b/lib/v5/pages/sort/functions/insertion.dart @@ -0,0 +1,19 @@ +import '../functions.dart'; + +///插入排序 +Future insertionSort(List src, SortCallback callback) async { + for (int i = 1; i < src.length; i++) { + int temp = src[i]; // 将当前元素存储到临时变量 temp 中 + int j = i - 1; // j 表示已排序部分的最后一个元素的索引 + + // 在已排序部分从后往前查找,找到合适位置插入当前元素 + while (j >= 0 && temp < src[j]) { + src[j + 1] = src[j]; // 当前元素比已排序部分的元素小,将元素后移一位 + --j; // 向前遍历 + // 更新排序结果回调 + await callback(src); + } + src[j + 1] = temp; // 插入当前元素到已排序部分的正确位置 + await callback(src); + } +} diff --git a/lib/v5/pages/sort/functions/merage.dart b/lib/v5/pages/sort/functions/merage.dart new file mode 100644 index 0000000..12135b4 --- /dev/null +++ b/lib/v5/pages/sort/functions/merage.dart @@ -0,0 +1,79 @@ +import '../functions.dart'; + +//快速排序 +Future mergeSort(List src, SortCallback callback) async { + await _mergeSort(src,callback,0,src.length-1); +} + +///归并排序 +Future _mergeSort(List src, SortCallback callback,int leftIndex, int rightIndex) async { + // 定义一个名为 merge 的异步函数,用于合并两个有序子数组 + Future merge(int leftIndex, int middleIndex, int rightIndex) async { + // 计算左侧子数组和右侧子数组的大小 + int leftSize = middleIndex - leftIndex + 1; + int rightSize = rightIndex - middleIndex; + + // 创建左侧子数组和右侧子数组 + List leftList = List.generate(leftSize, (index) => 0); + List rightList = List.generate(rightSize, (index) => 0); + + // 将原始数组中的元素分别复制到左侧子数组和右侧子数组中 + for (int i = 0; i < leftSize; i++) { + leftList[i] = src[leftIndex + i]; + } + for (int j = 0; j < rightSize; j++) { + rightList[j] = src[middleIndex + j + 1]; + } + + // 初始化游标和索引 + int i = 0, j = 0; + int k = leftIndex; + + // 比较左侧子数组和右侧子数组的元素,并按顺序将较小的元素放入原始数组中 + while (i < leftSize && j < rightSize) { + if (leftList[i] <= rightList[j]) { + src[k] = leftList[i]; + i++; + } else { + src[k] = rightList[j]; + j++; + } + + await callback(src); + + k++; + } + + // 将左侧子数组或右侧子数组中剩余的元素放入原始数组中 + while (i < leftSize) { + src[k] = leftList[i]; + i++; + k++; + + await callback(src); + } + + while (j < rightSize) { + src[k] = rightList[j]; + j++; + k++; + + await callback(src); + } + } + + // 如果左索引小于右索引,则递归地对数组进行归并排序 + if (leftIndex < rightIndex) { + // 计算中间索引位置 + int middleIndex = (rightIndex + leftIndex) ~/ 2; + + // 分别对左侧子数组和右侧子数组进行归并排序 + await _mergeSort(src,callback,leftIndex, middleIndex); + await _mergeSort(src,callback,middleIndex + 1, rightIndex); + + await callback(src); + + // 合并两个有序子数组 + await merge(leftIndex, middleIndex, rightIndex); + } +} \ No newline at end of file diff --git a/lib/v5/pages/sort/functions/oddEven.dart b/lib/v5/pages/sort/functions/oddEven.dart new file mode 100644 index 0000000..bd6f548 --- /dev/null +++ b/lib/v5/pages/sort/functions/oddEven.dart @@ -0,0 +1,37 @@ +import '../functions.dart'; + +///奇偶排序(Odd-Even Sort) +Future oddEvenSort(List src, SortCallback callback) async { + bool isSorted = false; + + while (!isSorted) { + // 当 isSorted 为 false 时执行循环 + isSorted = true; // 先假设数组已经排好序 + + for (int i = 1; i <= src.length - 2; i = i + 2) { + // 对奇数索引位置进行比较 + if (src[i] > src[i + 1]) { + // 如果当前元素大于后面的元素,则交换它们的值 + int temp = src[i]; + src[i] = src[i + 1]; + src[i + 1] = temp; + isSorted = false; // 若发生了交换,则说明数组仍未完全排序,将 isSorted 设为 false + await callback(src); + } + } + + for (int i = 0; i <= src.length - 2; i = i + 2) { + // 对偶数索引位置进行比较 + if (src[i] > src[i + 1]) { + // 如果当前元素大于后面的元素,则交换它们的值 + int temp = src[i]; + src[i] = src[i + 1]; + src[i + 1] = temp; + isSorted = false; + await callback(src); + } + } + } + + return; +} \ No newline at end of file diff --git a/lib/v5/pages/sort/functions/pigeonHole.dart b/lib/v5/pages/sort/functions/pigeonHole.dart new file mode 100644 index 0000000..83bbce1 --- /dev/null +++ b/lib/v5/pages/sort/functions/pigeonHole.dart @@ -0,0 +1,33 @@ +import '../functions.dart'; + +///鸽巢排序 +Future pigeonHoleSort(List src, SortCallback callback ) async{ + int min = src[0]; + int max = src[0]; + int range, i, j, index; + + // 找到数组中的最大值和最小值 + for (int a = 0; a < src.length; a++) { + if (src[a] > max) max = src[a]; + if (src[a] < min) min = src[a]; + } + + // 计算鸽巢的个数 + range = max - min + 1; + List p = List.generate(range, (i) => 0); + + // 将数字分配到各个鸽巢中 + for (i = 0; i < src.length; i++) { + p[src[i] - min]++; + } + + index = 0; + + // 将鸽巢中的数字取出,重新放回到数组中 + for (j = 0; j < range; j++) { + while (p[j]-- > 0) { + src[index++] = j + min; + await callback(src); + } + } +} \ No newline at end of file diff --git a/lib/v5/pages/sort/functions/quick.dart b/lib/v5/pages/sort/functions/quick.dart new file mode 100644 index 0000000..25b9685 --- /dev/null +++ b/lib/v5/pages/sort/functions/quick.dart @@ -0,0 +1,65 @@ +import '../functions.dart'; + +//快速排序 +Future quickSort(List src, SortCallback callback) async { + await _quickSort(src,callback,0,src.length-1); +} + +///快速排序 +Future _quickSort(List src, SortCallback callback,int leftIndex,int rightIndex) async { + // 定义一个名为 _partition 的异步函数,用于划分数组,并返回划分后的基准元素的索引位置 + Future _partition(int left, int right) async { + // 选择中间位置的元素作为基准元素 + int p = (left + (right - left) / 2).toInt(); + + // 交换基准元素和最右边的元素 + var temp = src[p]; + src[p] = src[right]; + src[right] = temp; + await callback(src); + + // 初始化游标 cursor + int cursor = left; + + // 遍历数组并根据基准元素将元素交换到左侧或右侧 + for (int i = left; i < right; i++) { + if (cf(src[i], src[right]) <= 0) { + // 如果当前元素小于等于基准元素,则交换它和游标位置的元素 + var temp = src[i]; + src[i] = src[cursor]; + src[cursor] = temp; + cursor++; + await callback(src); + } + } + + // 将基准元素放置在游标位置 + temp = src[right]; + src[right] = src[cursor]; + src[cursor] = temp; + + await callback(src); + + return cursor; // 返回基准元素的索引位置 + } + + // 如果左索引小于右索引,则递归地对数组进行快速排序 + if (leftIndex < rightIndex) { + int p = await _partition(leftIndex, rightIndex); + + await _quickSort(src,callback,leftIndex, p - 1); // 对基准元素左侧的子数组进行快速排序 + + await _quickSort(src,callback, p + 1, rightIndex); // 对基准元素右侧的子数组进行快速排序 + } +} + +// 比较函数,用于判断两个元素的大小关系 +cf(int a, int b) { + if (a < b) { + return -1; // 若 a 小于 b,则返回 -1 + } else if (a > b) { + return 1; // 若 a 大于 b,则返回 1 + } else { + return 0; // 若 a 等于 b,则返回 0 + } +} \ No newline at end of file diff --git a/lib/v5/pages/sort/functions/selection.dart b/lib/v5/pages/sort/functions/selection.dart new file mode 100644 index 0000000..185dae2 --- /dev/null +++ b/lib/v5/pages/sort/functions/selection.dart @@ -0,0 +1,18 @@ +import '../functions.dart'; + +///选择排序 +Future selectionSort(List src, SortCallback callback ) async { + for (int i = 0; i < src.length; i++) { + for (int j = i + 1; j < src.length; j++) { + // 遍历未排序部分,内层循环控制变量 j + if (src[i] > src[j]) { + // 判断当前元素是否比后续元素小 + int temp = src[j]; + // 交换当前元素和后续较小的元素 + src[j] = src[i]; + src[i] = temp; + } + await callback(src); + } + } +} \ No newline at end of file diff --git a/lib/v5/pages/sort/functions/shell.dart b/lib/v5/pages/sort/functions/shell.dart new file mode 100644 index 0000000..232f872 --- /dev/null +++ b/lib/v5/pages/sort/functions/shell.dart @@ -0,0 +1,21 @@ +import '../functions.dart'; + +///希尔排序 +Future shellSort(List src, SortCallback callback) async{ + //定义变量 gap 并初始化为数组长度的一半。每次循环完成后将 gap 减半直到等于 0。 + for (int gap = src.length ~/ 2; gap > 0; gap ~/= 2) { + //遍历每个子序列并进行插入排序。初始时从第一个子序列的第二个元素开始,即 i = gap,以 gap 为步长逐个遍历每个子序列。 + for (int i = gap; i < src.length; i += 1) { + //将当前遍历到的元素赋值给它 + int temp = src[i]; + //内部使用一个 for 循环来实现插入排序。 + //循环开始时定义变量 j 并将其初始化为当前遍历到的元素的下标。通过不断比较前后相隔 gap 的元素大小并交换位置,将当前元素插入到正确的位置。 + int j; + for (j = i; j >= gap && src[j - gap] > temp; j -= gap) { + src[j] = src[j - gap]; + } + src[j] = temp; + await callback(src); + } + } +} \ No newline at end of file diff --git a/lib/v5/pages/sort/provider/sort_config.dart b/lib/v5/pages/sort/provider/sort_config.dart new file mode 100644 index 0000000..2ad50ed --- /dev/null +++ b/lib/v5/pages/sort/provider/sort_config.dart @@ -0,0 +1,31 @@ +import 'dart:ui'; + +import 'package:flutter/material.dart'; + +class SortConfig { + final int count; + final int seed; + final Duration duration; + final String name; + + SortConfig({ + this.count = 100, + this.duration = const Duration(microseconds: 1500), + this.seed = -1, + this.name = 'insertion', + }); + + SortConfig copyWith({ + int? count, + int? seed, + Duration? duration, + String? name, + }) => + SortConfig( + count:count??this.count, + seed:seed??this.seed, + duration:duration??this.duration, + name:name??this.name, + ); +} + diff --git a/lib/v5/pages/sort/provider/state.dart b/lib/v5/pages/sort/provider/state.dart new file mode 100644 index 0000000..e60e208 --- /dev/null +++ b/lib/v5/pages/sort/provider/state.dart @@ -0,0 +1,78 @@ +import 'dart:math'; + +import 'package:flutter/cupertino.dart'; + +import '../functions.dart'; +import 'sort_config.dart'; + +enum SortStatus{ + none, // 未操作 + sorting, // 排序中 + sorted, // 排序完成 +} + +class SortState with ChangeNotifier{ + + SortState(){ + reset(); + } + + SortStatus status = SortStatus.none; + + List data = []; + List stepData = []; + + SortConfig _config = SortConfig(); + SortConfig get config => _config; + Random random = Random(); + + set config(SortConfig config){ + _config = config; + reset(); + notifyListeners(); + } + + + void reset(){ + data.clear(); + status = SortStatus.none; + notifyListeners(); + int count = config.count; + if(config.seed!=-1){ + random = Random(config.seed); + } + for (int i = 0; i < count; i++) { + //随机往数组中填值 + data.add(random.nextInt(1000)); + } + } + + void sort() async{ + status = SortStatus.sorting; + notifyListeners(); + Stopwatch stopwatch = Stopwatch()..start(); + SortFunction? sortFunction = sortFunctionMap[config.name]; + if(sortFunction!=null){ + await sortFunction(data,(arr) async { + await Future.delayed(config.duration); + notifyListeners(); + }); + } + status = SortStatus.sorted; + notifyListeners(); + stopwatch.stop(); + print("Sorting completed in ${stopwatch.elapsed.inMilliseconds} ms."); + } +} + +/// Provides the current [SortState] to descendant widgets in the tree. +class SortStateScope extends InheritedNotifier { + const SortStateScope({ + required super.notifier, + required super.child, + super.key, + }); + + static SortState of(BuildContext context) => + context.dependOnInheritedWidgetOfExactType()!.notifier!; +} \ No newline at end of file diff --git a/lib/v5/pages/sort/sort_page.dart b/lib/v5/pages/sort/sort_page.dart deleted file mode 100644 index 8172cc0..0000000 --- a/lib/v5/pages/sort/sort_page.dart +++ /dev/null @@ -1,859 +0,0 @@ -import 'dart:async'; -import 'dart:math'; - -import 'package:flutter/material.dart'; - -class SortPage extends StatefulWidget { - const SortPage({Key? key}) : super(key: key); - - @override - State createState() => _SortPageState(); -} - -class _SortPageState extends State { - //存放随机数组 - List numbers = []; - - //订阅流 - StreamController> streamController = StreamController(); - String currentSort = 'bubble'; - - //柱子的数量 -> 生成排序数组的长度 - double sampleSize = 0; - - //是否排序 - bool isSorted = false; - - //是否在排序中 - bool isSorting = false; - - //排序动画更新的速度 - int speed = 0; - - static int duration = 1500; - - String getTitle() { - switch (currentSort) { - case "bubble": - return "Bubble Sort"; - case "coctail": - return "Coctail Sort"; - case "comb": - return "Comb Sort"; - case "pigeonhole": - return "Pigeonhole Sort"; - case "shell": - return "Shell Sort"; - case "selection": - return "Selection Sort"; - case "cycle": - return "Cycle Sort"; - case "heap": - return "Heap Sort"; - case "insertion": - return "Insertion Sort"; - case "gnome": - return "Gnome Sort"; - case "oddeven": - return "OddEven Sort"; - case "quick": - return "Quick Sort"; - case "merge": - return "Merge Sort"; - } - return ""; - } - - reset() { - isSorted = false; - numbers = []; - for (int i = 0; i < sampleSize; ++i) { - numbers.add(Random().nextInt(500)); - } - streamController.add(numbers); - } - - Duration getDuration() { - return Duration(microseconds: duration); - } - - ///动画时间 - changeSpeed() { - if (speed >= 3) { - speed = 0; - duration = 1500; - } else { - speed++; - duration = duration ~/ 2; - } - setState(() {}); - } - - ///冒泡排序 - bubbleSort() async { - //控制需要进行排序的次数。每一轮循环都会确定一个数字的最终位置。 - for (int i = 0; i < numbers.length; ++i) { - //遍历当前未排序的元素,通过相邻的元素比较并交换位置来完成排序。 - for (int j = 0; j < numbers.length - i - 1; ++j) { - //如果 _numbers[j] 大于 _numbers[j + 1],则交换它们的位置,确保较大的元素移到右边。 - if (numbers[j] > numbers[j + 1]) { - int temp = numbers[j]; - numbers[j] = numbers[j + 1]; - numbers[j + 1] = temp; - } - //实现一个延迟,以便在ui上展示排序的动画效果 - await Future.delayed(getDuration(), () {}); - streamController.add(numbers); - } - } - } - - ///鸡尾酒排序(双向冒泡排序) - cocktailSort() async { - bool swapped = true; // 表示是否进行了交换 - int start = 0; // 当前未排序部分的起始位置 - int end = numbers.length; // 当前未排序部分的结束位置 - - // 开始排序循环,只有当没有进行交换时才会退出循环 - while (swapped == true) { - swapped = false; - - // 从左往右遍历需要排序的部分 - for (int i = start; i < end - 1; ++i) { - // 对每两个相邻元素进行比较 - if (numbers[i] > numbers[i + 1]) { - // 如果前面的元素大于后面的元素,则交换它们的位置 - int temp = numbers[i]; - numbers[i] = numbers[i + 1]; - numbers[i + 1] = temp; - swapped = true; // 进行了交换 - } - - // 实现动画效果,延迟一段时间后更新数组状态 - await Future.delayed(getDuration()); - streamController.add(numbers); - } - - // 如果没有进行交换,则说明已经排好序,退出循环 - if (swapped == false) break; - // 重设为false,准备进行下一轮排序 - swapped = false; - // 将end设置为上一轮排序的最后一个元素的位置 - end = end - 1; - - // 从右往左遍历需要排序的部分 - for (int i = end - 1; i >= start; i--) { - // 对每两个相邻元素进行比较 - if (numbers[i] > numbers[i + 1]) { - // 如果前面的元素大于后面的元素,则交换它们的位置 - int temp = numbers[i]; - numbers[i] = numbers[i + 1]; - numbers[i + 1] = temp; - swapped = true; // 进行了交换 - } - - // 实现动画效果,延迟一段时间后更新数组状态 - await Future.delayed(getDuration()); - streamController.add(numbers); - } - // 将start向右移一位,准备下一轮排序 - start = start + 1; - } - } - - ///梳排序(Comb Sort) - combSort() async { - int gap = numbers.length; - - bool swapped = true; - - // 当间隔不为1或存在交换时执行循环 - while (gap != 1 || swapped == true) { - // 通过缩小间隔来逐步将元素归位 - gap = getNextGap(gap); - swapped = false; - for (int i = 0; i < numbers.length - gap; i++) { - // 如果当前元素大于间隔位置上的元素,则交换它们的位置 - if (numbers[i] > numbers[i + gap]) { - int temp = numbers[i]; - numbers[i] = numbers[i + gap]; - numbers[i + gap] = temp; - swapped = true; - } - - // 实现一个延迟,以便在 UI 上展示排序的动画效果。 - await Future.delayed(getDuration()); - streamController.add(numbers); - } - } - } - - int getNextGap(int gap) { - // 根据当前间隔值计算下一个间隔值 - gap = (gap * 10) ~/ 13; - if (gap < 1) return 1; - return gap; - } - - ///鸽巢排序 - pigeonHole() async { - int min = numbers[0]; - int max = numbers[0]; - int range, i, j, index; - - // 找到数组中的最大值和最小值 - for (int a = 0; a < numbers.length; a++) { - if (numbers[a] > max) max = numbers[a]; - if (numbers[a] < min) min = numbers[a]; - } - - // 计算鸽巢的个数 - range = max - min + 1; - List p = List.generate(range, (i) => 0); - - // 将数字分配到各个鸽巢中 - for (i = 0; i < numbers.length; i++) { - p[numbers[i] - min]++; - } - - index = 0; - - // 将鸽巢中的数字取出,重新放回到数组中 - for (j = 0; j < range; j++) { - while (p[j]-- > 0) { - numbers[index++] = j + min; - await Future.delayed(getDuration()); - streamController.add(numbers); - } - } - } - - ///希尔排序 - shellSort() async { - //定义变量 gap 并初始化为数组长度的一半。每次循环完成后将 gap 减半直到等于 0。 - for (int gap = numbers.length ~/ 2; gap > 0; gap ~/= 2) { - //遍历每个子序列并进行插入排序。初始时从第一个子序列的第二个元素开始,即 i = gap,以 gap 为步长逐个遍历每个子序列。 - for (int i = gap; i < numbers.length; i += 1) { - //将当前遍历到的元素赋值给它 - int temp = numbers[i]; - //内部使用一个 for 循环来实现插入排序。 - //循环开始时定义变量 j 并将其初始化为当前遍历到的元素的下标。通过不断比较前后相隔 gap 的元素大小并交换位置,将当前元素插入到正确的位置。 - int j; - for (j = i; j >= gap && numbers[j - gap] > temp; j -= gap) { - numbers[j] = numbers[j - gap]; - } - numbers[j] = temp; - await Future.delayed(getDuration()); - streamController.add(numbers); - } - } - } - - ///选择排序 - selectionSort() async { - for (int i = 0; i < numbers.length; i++) { - for (int j = i + 1; j < numbers.length; j++) { - // 遍历未排序部分,内层循环控制变量 j - if (numbers[i] > numbers[j]) { - // 判断当前元素是否比后续元素小 - int temp = numbers[j]; - // 交换当前元素和后续较小的元素 - numbers[j] = numbers[i]; - numbers[i] = temp; - } - - await Future.delayed(getDuration(), () {}); - - streamController.add(numbers); - } - } - } - - ///循环排序 - cycleSort() async { - int writes = 0; - for (int cycleStart = 0; cycleStart <= numbers.length - 2; cycleStart++) { - int item = numbers[cycleStart]; - int pos = cycleStart; - - // 在未排序部分中寻找比当前元素小的元素个数 - for (int i = cycleStart + 1; i < numbers.length; i++) { - if (numbers[i] < item) pos++; - } - - // 如果当前元素已经在正确位置上,则跳过此次迭代 - if (pos == cycleStart) { - continue; - } - - // 将当前元素放置到正确的位置上,并记录写操作次数 - while (item == numbers[pos]) { - pos += 1; - } - if (pos != cycleStart) { - int temp = item; - item = numbers[pos]; - numbers[pos] = temp; - writes++; - } - - // 循环将位于当前位置的元素放置到正确的位置上 - while (pos != cycleStart) { - pos = cycleStart; - // 继续在未排序部分中寻找比当前元素小的元素个数 - for (int i = cycleStart + 1; i < numbers.length; i++) { - if (numbers[i] < item) pos += 1; - } - - // 将当前元素放置到正确的位置上,并记录写操作次数 - while (item == numbers[pos]) { - pos += 1; - } - if (item != numbers[pos]) { - int temp = item; - item = numbers[pos]; - numbers[pos] = temp; - writes++; - } - - // 添加延迟操作以展示排序过程 - await Future.delayed(getDuration()); - streamController.add(numbers); - } - } - } - - ///堆排序 - heapSort() async { - // 从最后一个非叶子节点开始,构建最大堆 - for (int i = numbers.length ~/ 2; i >= 0; i--) { - await heapify(numbers, numbers.length, i); - streamController.add(numbers); - } - - // 依次取出最大堆的根节点(最大值),并进行堆化 - for (int i = numbers.length - 1; i >= 0; i--) { - int temp = numbers[0]; - numbers[0] = numbers[i]; - numbers[i] = temp; - await heapify(numbers, i, 0); - streamController.add(numbers); - } - } - - heapify(List arr, int n, int i) async { - int largest = i; - int l = 2 * i + 1; // 左子节点索引 - int r = 2 * i + 2; // 右子节点索引 - - // 如果左子节点存在并且大于父节点,则更新最大值索引 - if (l < n && arr[l] > arr[largest]) largest = l; - - // 如果右子节点存在并且大于父节点或左子节点,则更新最大值索引 - if (r < n && arr[r] > arr[largest]) largest = r; - - // 如果最大值索引不等于当前节点索引,则交换节点值,并递归进行堆化 - if (largest != i) { - int temp = numbers[i]; - numbers[i] = numbers[largest]; - numbers[largest] = temp; - heapify(arr, n, largest); - } - - await Future.delayed(getDuration()); // 延迟操作,用于可视化排序过程 - } - - ///插入排序 - insertionSort() async { - for (int i = 1; i < numbers.length; i++) { - int temp = numbers[i]; // 将当前元素存储到临时变量 temp 中 - int j = i - 1; // j 表示已排序部分的最后一个元素的索引 - - // 在已排序部分从后往前查找,找到合适位置插入当前元素 - while (j >= 0 && temp < numbers[j]) { - numbers[j + 1] = numbers[j]; // 当前元素比已排序部分的元素小,将元素后移一位 - --j; // 向前遍历 - await Future.delayed(getDuration()); - streamController.add(numbers); // 更新排序结果 - } - - numbers[j + 1] = temp; // 插入当前元素到已排序部分的正确位置 - await Future.delayed(getDuration(), () {}); - streamController.add(numbers); // 更新排序结果 - } - } - - ///地精排序 (侏儒排序) - gnomeSort() async { - int index = 0; - - while (index < numbers.length) { - // 当 index 小于数组长度时执行循环 - if (index == 0) index++; - if (numbers[index] >= numbers[index - 1]) { - // 如果当前元素大于等于前面的元素,则将 index 加1 - index++; - } else { - // 否则,交换这两个元素,并将 index 减1(使得元素可以沉到正确位置) - int temp = numbers[index]; - numbers[index] = numbers[index - 1]; - numbers[index - 1] = temp; - index--; - } - await Future.delayed(getDuration()); - streamController.add(numbers); - } - - return; - } - - ///奇偶排序(Odd-Even Sort) - oddEvenSort() async { - bool isSorted = false; - - while (!isSorted) { - // 当 isSorted 为 false 时执行循环 - isSorted = true; // 先假设数组已经排好序 - - for (int i = 1; i <= numbers.length - 2; i = i + 2) { - // 对奇数索引位置进行比较 - if (numbers[i] > numbers[i + 1]) { - // 如果当前元素大于后面的元素,则交换它们的值 - int temp = numbers[i]; - numbers[i] = numbers[i + 1]; - numbers[i + 1] = temp; - isSorted = false; // 若发生了交换,则说明数组仍未完全排序,将 isSorted 设为 false - await Future.delayed(getDuration()); - streamController.add(numbers); - } - } - - for (int i = 0; i <= numbers.length - 2; i = i + 2) { - // 对偶数索引位置进行比较 - if (numbers[i] > numbers[i + 1]) { - // 如果当前元素大于后面的元素,则交换它们的值 - int temp = numbers[i]; - numbers[i] = numbers[i + 1]; - numbers[i + 1] = temp; - isSorted = false; - await Future.delayed(getDuration()); - streamController.add(numbers); - } - } - } - - return; - } - - ///快速排序 - quickSort(int leftIndex, int rightIndex) async { - // 定义一个名为 _partition 的异步函数,用于划分数组,并返回划分后的基准元素的索引位置 - Future _partition(int left, int right) async { - // 选择中间位置的元素作为基准元素 - int p = (left + (right - left) / 2).toInt(); - - // 交换基准元素和最右边的元素 - var temp = numbers[p]; - numbers[p] = numbers[right]; - numbers[right] = temp; - await Future.delayed(getDuration()); - streamController.add(numbers); - - // 初始化游标 cursor - int cursor = left; - - // 遍历数组并根据基准元素将元素交换到左侧或右侧 - for (int i = left; i < right; i++) { - if (cf(numbers[i], numbers[right]) <= 0) { - // 如果当前元素小于等于基准元素,则交换它和游标位置的元素 - var temp = numbers[i]; - numbers[i] = numbers[cursor]; - numbers[cursor] = temp; - cursor++; - - await Future.delayed(getDuration()); - streamController.add(numbers); - } - } - - // 将基准元素放置在游标位置 - temp = numbers[right]; - numbers[right] = numbers[cursor]; - numbers[cursor] = temp; - - await Future.delayed(getDuration()); - streamController.add(numbers); - - return cursor; // 返回基准元素的索引位置 - } - - // 如果左索引小于右索引,则递归地对数组进行快速排序 - if (leftIndex < rightIndex) { - int p = await _partition(leftIndex, rightIndex); - - await quickSort(leftIndex, p - 1); // 对基准元素左侧的子数组进行快速排序 - - await quickSort(p + 1, rightIndex); // 对基准元素右侧的子数组进行快速排序 - } - } - - // 比较函数,用于判断两个元素的大小关系 - cf(int a, int b) { - if (a < b) { - return -1; // 若 a 小于 b,则返回 -1 - } else if (a > b) { - return 1; // 若 a 大于 b,则返回 1 - } else { - return 0; // 若 a 等于 b,则返回 0 - } - } - - ///归并排序 - mergeSort(int leftIndex, int rightIndex) async { - // 定义一个名为 merge 的异步函数,用于合并两个有序子数组 - Future merge(int leftIndex, int middleIndex, int rightIndex) async { - // 计算左侧子数组和右侧子数组的大小 - int leftSize = middleIndex - leftIndex + 1; - int rightSize = rightIndex - middleIndex; - - // 创建左侧子数组和右侧子数组 - List leftList = List.generate(leftSize, (index) => 0); - List rightList = List.generate(rightSize, (index) => 0); - - // 将原始数组中的元素分别复制到左侧子数组和右侧子数组中 - for (int i = 0; i < leftSize; i++) { - leftList[i] = numbers[leftIndex + i]; - } - for (int j = 0; j < rightSize; j++) { - rightList[j] = numbers[middleIndex + j + 1]; - } - - // 初始化游标和索引 - int i = 0, j = 0; - int k = leftIndex; - - // 比较左侧子数组和右侧子数组的元素,并按顺序将较小的元素放入原始数组中 - while (i < leftSize && j < rightSize) { - if (leftList[i] <= rightList[j]) { - numbers[k] = leftList[i]; - i++; - } else { - numbers[k] = rightList[j]; - j++; - } - - await Future.delayed(getDuration()); - streamController.add(numbers); - - k++; - } - - // 将左侧子数组或右侧子数组中剩余的元素放入原始数组中 - while (i < leftSize) { - numbers[k] = leftList[i]; - i++; - k++; - - await Future.delayed(getDuration()); - streamController.add(numbers); - } - - while (j < rightSize) { - numbers[k] = rightList[j]; - j++; - k++; - - await Future.delayed(getDuration()); - streamController.add(numbers); - } - } - - // 如果左索引小于右索引,则递归地对数组进行归并排序 - if (leftIndex < rightIndex) { - // 计算中间索引位置 - int middleIndex = (rightIndex + leftIndex) ~/ 2; - - // 分别对左侧子数组和右侧子数组进行归并排序 - await mergeSort(leftIndex, middleIndex); - await mergeSort(middleIndex + 1, rightIndex); - - await Future.delayed(getDuration()); - streamController.add(numbers); - - // 合并两个有序子数组 - await merge(leftIndex, middleIndex, rightIndex); - } - } - - checkAndResetIfSorted() async { - if (isSorted) { - reset(); - await Future.delayed(const Duration(milliseconds: 200)); - } - } - - sort() async { - setState(() { - isSorting = true; - }); - - await checkAndResetIfSorted(); - - Stopwatch stopwatch = Stopwatch()..start(); - - switch (currentSort) { - case "bubble": - await bubbleSort(); - break; - case "coctail": - await cocktailSort(); - break; - case "comb": - await combSort(); - break; - case "pigeonhole": - await pigeonHole(); - break; - case "shell": - await shellSort(); - break; - case "selection": - await selectionSort(); - break; - case "cycle": - await cycleSort(); - break; - case "heap": - await heapSort(); - break; - case "insertion": - await insertionSort(); - break; - case "gnome": - await gnomeSort(); - break; - case "oddeven": - await oddEvenSort(); - break; - case "quick": - await quickSort(0, sampleSize.toInt() - 1); - break; - case "merge": - await mergeSort(0, sampleSize.toInt() - 1); - break; - } - - stopwatch.stop(); - - print("Sorting completed in ${stopwatch.elapsed.inMilliseconds} ms."); - setState(() { - isSorting = false; - isSorted = true; - }); - } - - setSort(String type) { - setState(() { - currentSort = type; - }); - } - - @override - void initState() { - super.initState(); - // reset(); - } - - @override - void didChangeDependencies() { - super.didChangeDependencies(); - sampleSize = MediaQuery.of(context).size.width / 2; - for (int i = 0; i < sampleSize; ++i) { - //随机往数组中填值 - numbers.add(Random().nextInt(500)); - } - setState(() {}); - } - - @override - void dispose() { - streamController.close(); - super.dispose(); - } - - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar( - title: Text( - "当前选择的是:${getTitle()}", - style: const TextStyle(fontSize: 14), - ), - actions: [ - PopupMenuButton( - initialValue: currentSort, - itemBuilder: (ctx) { - return const [ - PopupMenuItem( - value: 'bubble', - child: Text("Bubble Sort — 冒泡排序"), - ), - PopupMenuItem( - value: 'coctail', - child: Text("Coctail Sort — 鸡尾酒排序(双向冒泡排序)"), - ), - PopupMenuItem( - value: 'comb', - child: Text("Comb Sort — 梳排序"), - ), - PopupMenuItem( - value: 'pigeonhole', - child: Text("pigeonhole Sort — 鸽巢排序"), - ), - PopupMenuItem( - value: 'shell', - child: Text("shell Sort — 希尔排序"), - ), - PopupMenuItem( - value: 'selection', - child: Text("Selection Sort — 选择排序"), - ), - PopupMenuItem( - value: 'cycle', - child: Text("CycleSort — 循环排序"), - ), - PopupMenuItem( - value: 'heap', - child: Text("HeapSort — 堆排序"), - ), - PopupMenuItem( - value: 'insertion', - child: Text("InsertionSort — 插入排序"), - ), - PopupMenuItem( - value: 'gnome', - child: Text("GnomeSort — 地精排序 (侏儒排序)"), - ), - PopupMenuItem( - value: 'oddeven', - child: Text("OddEvenSort — 奇偶排序"), - ), - PopupMenuItem( - value: 'quick', - child: Text("QuickSort — 快速排序"), - ), - PopupMenuItem( - value: 'merge', - child: Text("MergeSort — 归并排序"), - ), - ]; - }, - onSelected: (String value) { - reset(); - setSort(value); - }, - ) - ], - ), - body: StreamBuilder( - initialData: numbers, - stream: streamController.stream, - builder: (context, snapshot) { - List numbers = snapshot.data as List; - int counter = 0; - return Row( - children: numbers.map((int num) { - counter++; - return CustomPaint( - painter: BarPainter( - width: MediaQuery.of(context).size.width / sampleSize, - value: num, - index: counter, - ), - ); - }).toList(), - ); - }, - ), - bottomNavigationBar: BottomAppBar( - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceAround, - children: [ - ElevatedButton( - onPressed: isSorting - ? null - : () { - reset(); - setSort(currentSort); - }, - child: const Text("重置")), - ElevatedButton( - onPressed: isSorting ? null : sort, child: const Text("开始排序")), - ElevatedButton( - onPressed: isSorting ? null : changeSpeed, - child: Text( - "${speed + 1}x", - style: const TextStyle(fontSize: 20), - ), - ), - ], - ), - ), - ); - } -} - -class BarPainter extends CustomPainter { - //宽度 - final double width; - - //高度(数组中对应的值) - final int value; - - //位置索引 - final int index; - - BarPainter({required this.width, required this.value, required this.index}); - - @override - void paint(Canvas canvas, Size size) { - Paint paint = Paint(); - if (value < 500 * .10) { - paint.color = Colors.blue.shade100; - } else if (value < 500 * .20) { - paint.color = Colors.blue.shade200; - } else if (value < 500 * .30) { - paint.color = Colors.blue.shade300; - } else if (value < 500 * .40) { - paint.color = Colors.blue.shade400; - } else if (value < 500 * .50) { - paint.color = Colors.blue.shade500; - } else if (value < 500 * .60) { - paint.color = Colors.blue.shade600; - } else if (value < 500 * .70) { - paint.color = Colors.blue.shade700; - } else if (value < 500 * .80) { - paint.color = Colors.blue.shade800; - } else if (value < 500 * .90) { - paint.color = Colors.blue.shade900; - } else { - paint.color = const Color(0xFF011E51); - } - - paint.strokeWidth = width; - paint.strokeCap = StrokeCap.round; - - canvas.drawLine( - Offset(index * width, 0), - Offset( - index * width, - value.ceilToDouble(), - ), - paint); - } - - @override - bool shouldRepaint(covariant CustomPainter oldDelegate) { - return true; - } -} diff --git a/lib/v5/pages/sort/sort_setting.dart b/lib/v5/pages/sort/sort_setting.dart new file mode 100644 index 0000000..3b61da8 --- /dev/null +++ b/lib/v5/pages/sort/sort_setting.dart @@ -0,0 +1,109 @@ +import 'package:flutter/material.dart'; + +import 'provider/sort_config.dart'; +import 'provider/state.dart'; + +class SortSettings extends StatefulWidget { + + const SortSettings({super.key,}); + + @override + State createState() => _SortSettingsState(); +} + +class _SortSettingsState extends State { + late TextEditingController _count = + TextEditingController(); + late TextEditingController _duration = TextEditingController(); + late TextEditingController _seed = + TextEditingController(); + + @override + void initState() { + super.initState(); + } + + @override + void didChangeDependencies() { + print('========_SortSettingsState#didChangeDependencies============='); + super.didChangeDependencies(); + SortState state = SortStateScope.of(context); + _count.text = state.config.count.toString(); + _duration.text = state.config.duration.inMicroseconds.toString(); + _seed.text = state.config.seed.toString(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + backgroundColor: Colors.white, + leading: BackButton(), + titleTextStyle: TextStyle( + color: Colors.black, + fontSize: 16, + fontWeight: FontWeight.bold, + ), + centerTitle: true, + title: Text('排序算法配置'), + ), + body: Padding( + padding: const EdgeInsets.all(8.0), + child: Column( + children: [ + Row( + children: [ + Text('数据数量(个数):'), + const SizedBox( + width: 20, + ), + Expanded( + child: TextField( + controller: _count, + )), + ], + ), + Row( + children: [ + Text('时间间隔(微秒):'), + const SizedBox( + width: 20, + ), + Expanded( + child: TextField( + controller: _duration, + )), + ], + ), + Row( + children: [ + Text('随机种子:'), + const SizedBox( + width: 20, + ), + Expanded( + child: TextField( + controller: _seed, + )), + ], + ), + Spacer(), + ElevatedButton( + onPressed: () { + SortState state = SortStateScope.of(context); + state.config =state.config.copyWith( + count: int.parse(_count.text), + duration: Duration( + microseconds: int.parse(_duration.text), + ), + seed: int.parse(_seed.text) + ); + Navigator.of(context).pop(); + }, + child: Text('确定设置')) + ], + ), + ), + ); + } +} diff --git a/lib/v5/pages/sort/views/data_painter.dart b/lib/v5/pages/sort/views/data_painter.dart new file mode 100644 index 0000000..d23e2f5 --- /dev/null +++ b/lib/v5/pages/sort/views/data_painter.dart @@ -0,0 +1,60 @@ +import 'package:flutter/material.dart'; + + + +class DataPainter extends CustomPainter{ + + final List data; + + DataPainter({required this.data}); + + @override + void paint(Canvas canvas, Size size) { + double itemWidth = size.width/data.length; + double height = size.height; + + Paint paint = Paint(); + paint.strokeWidth = itemWidth; + paint.strokeCap = StrokeCap.round; + + for(int i=0;iAlertDialog()); + }, + child: const Icon(Icons.settings)) + ], + ); + } +} diff --git a/lib/v5/pages/sort/views/sort_button.dart b/lib/v5/pages/sort/views/sort_button.dart new file mode 100644 index 0000000..ec2b30a --- /dev/null +++ b/lib/v5/pages/sort/views/sort_button.dart @@ -0,0 +1,40 @@ +import 'package:flutter/material.dart'; + +import '../provider/state.dart'; + +class SortButton extends StatelessWidget { + const SortButton({super.key}); + + @override + Widget build(BuildContext context) { + SortState state = SortStateScope.of(context); + VoidCallback? action; + IconData icon; + Color color; + switch (state.status) { + case SortStatus.none: + icon = Icons.not_started_outlined; + color = Colors.green; + action = state.sort; + break; + case SortStatus.sorting: + icon = Icons.stop_circle_outlined; + color = Colors.grey; + action = null; + break; + case SortStatus.sorted: + icon = Icons.refresh; + color = Colors.black; + action = state.reset; + break; + } + + return GestureDetector( + onTap: action, + child: Icon( + icon, + color: color, + ), + ); + } +} diff --git a/lib/v5/pages/sort/views/sort_page.dart b/lib/v5/pages/sort/views/sort_page.dart new file mode 100644 index 0000000..01224c3 --- /dev/null +++ b/lib/v5/pages/sort/views/sort_page.dart @@ -0,0 +1,22 @@ + +import 'package:flutter/material.dart'; + +import '../provider/state.dart'; +import 'data_painter.dart'; + +class SortPage extends StatelessWidget{ + + @override + Widget build(BuildContext context) { + SortState state = SortStateScope.of(context); + List numbers = state.data; + + return Scaffold( + body: CustomPaint( + painter: DataPainter(data: numbers), + child: ConstrainedBox(constraints: BoxConstraints.expand()), + ), + ); + } +} + diff --git a/pubspec.yaml b/pubspec.yaml index 897e528..d4f622c 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -28,21 +28,23 @@ environment: # the latest version available on pub.dev. To see which dependencies have newer # versions available, run `flutter pub outdated`. dependencies: - flutter: sdk: flutter flutter_colorpicker: ^1.0.3 window_manager: ^0.3.7 - go_router: ^12.0.0 + adaptive_navigation: ^0.0.4 + go_router: ^10.1.0 + provider: 6.0.5 + url_launcher: ^6.0.7 + equatable: ^2.0.5 + url_strategy: ^0.2.0 + quiver: ^3.1.0 + path_to_regexp: ^0.4.0 # The following adds the Cupertino Icons font to your application. # Use with the CupertinoIcons class for iOS style icons. cupertino_icons: ^1.0.2 - - logging: ^1.0.0 - provider: 6.0.5 - shared_preferences: ^2.0.11 - url_launcher: ^6.0.7 - adaptive_navigation: ^0.0.4 + toly_menu: + path: E:\Projects\Flutter\packages\toly_menu dev_dependencies: flutter_test: diff --git a/windows/runner/main.cpp b/windows/runner/main.cpp index c92ca90..ae006fd 100644 --- a/windows/runner/main.cpp +++ b/windows/runner/main.cpp @@ -26,7 +26,7 @@ int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev, FlutterWindow window(project); Win32Window::Point origin(10, 10); - Win32Window::Size size(640, 360); + Win32Window::Size size(1600, 1200); if (!window.Create(L"iroute", origin, size)) { return EXIT_FAILURE; }