diff --git a/lib/main.dart b/lib/main.dart index 38ea808..b0e45aa 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -4,7 +4,7 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:window_manager/window_manager.dart'; -import 'v10/app.dart'; +import 'v11/app.dart'; void main() { WidgetsFlutterBinding.ensureInitialized(); diff --git a/lib/v10/app/navigation/router/iroute.dart b/lib/v10/app/navigation/router/iroute.dart index ddfc8c3..8b13ea1 100644 --- a/lib/v10/app/navigation/router/iroute.dart +++ b/lib/v10/app/navigation/router/iroute.dart @@ -147,7 +147,6 @@ class CellIRoute extends IRouteNode { if (pageBuilder != null) { return pageBuilder!(context, config, child); } - print("======CellIRoute#createCellPage${config.pageKey}================="); return MaterialPage( child: child, key: config.pageKey, diff --git a/lib/v11/app/navigation/router/app_router_delegate.dart b/lib/v11/app/navigation/router/app_router_delegate.dart index cdc38e0..bc85d42 100644 --- a/lib/v11/app/navigation/router/app_router_delegate.dart +++ b/lib/v11/app/navigation/router/app_router_delegate.dart @@ -15,7 +15,7 @@ class AppRouterDelegate extends RouterDelegate /// 核心数据,路由配置数据列表 final List _configs = []; - String get path => current.uri.path; + String get path => current.uri.toString(); IRouteConfig get current => _configs.last; diff --git a/lib/v11/app/navigation/router/iroute.dart b/lib/v11/app/navigation/router/iroute.dart index ddfc8c3..c01cf88 100644 --- a/lib/v11/app/navigation/router/iroute.dart +++ b/lib/v11/app/navigation/router/iroute.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:iroute/v9/app/navigation/router/views/navigator_scope.dart'; import 'iroute_config.dart'; +import 'utils/path_utils.dart'; typedef IRoutePageBuilder = Page? Function( BuildContext context, @@ -55,7 +56,7 @@ abstract class IRouteNode { if (node.children.isNotEmpty) { target = prefix + target; - List nodes = node.children.where((e) => e.path == target).toList(); + List nodes = node.children.where((e) => _match(e.path,target)).toList(); bool match = nodes.isNotEmpty; if (match) { IRouteNode matched = nodes.first; @@ -69,6 +70,14 @@ abstract class IRouteNode { } return result; } + + bool _match(String path ,String target){ + if(!path.contains(':')){ + return path == target; + } + return patternToRegExp(path,[]).hasMatch(target); + } + } /// 优先调用 [pageBuilder] 构建 Page @@ -147,7 +156,6 @@ class CellIRoute extends IRouteNode { if (pageBuilder != null) { return pageBuilder!(context, config, child); } - print("======CellIRoute#createCellPage${config.pageKey}================="); return MaterialPage( child: child, key: config.pageKey, diff --git a/lib/v11/app/navigation/router/iroute_config.dart b/lib/v11/app/navigation/router/iroute_config.dart index f526e71..3555b27 100644 --- a/lib/v11/app/navigation/router/iroute_config.dart +++ b/lib/v11/app/navigation/router/iroute_config.dart @@ -10,6 +10,7 @@ class IRouteConfig { final Object? extra; final bool forResult; final Uri uri; + final Map? pathParams; final bool keepAlive; final RouteStyle routeStyle; final bool recordHistory; @@ -18,12 +19,13 @@ class IRouteConfig { this.extra, required this.uri, this.forResult = false, + this.pathParams, this.keepAlive = false, this.routeStyle = RouteStyle.replace, this.recordHistory = false, }); - String get path => uri.path; + String get path => uri.toString(); IRouteConfig copyWith({ Object? extra, diff --git a/lib/v11/app/navigation/router/routes.dart b/lib/v11/app/navigation/router/routes.dart index 356f8b2..60bde28 100644 --- a/lib/v11/app/navigation/router/routes.dart +++ b/lib/v11/app/navigation/router/routes.dart @@ -43,7 +43,7 @@ CellIRoute appRoute = CellIRoute( widget: SortSettings(), ), const IRoute( - path: '/app/sort/player', + path: '/app/sort/player/:name', widget: SortPlayer(), ), ], @@ -80,7 +80,7 @@ Map kRouteLabelMap = { '/app/color/detail': '颜色详情', '/app/counter': '计数器', '/app/sort': '排序算法', - '/app/sort/player': '演示', + '/app/sort/:name': '', '/app/sort/settings': '排序配置', '/app/user': '我的', '/app/settings': '系统设置', diff --git a/lib/v11/app/navigation/router/utils/path_utils.dart b/lib/v11/app/navigation/router/utils/path_utils.dart new file mode 100644 index 0000000..dff7feb --- /dev/null +++ b/lib/v11/app/navigation/router/utils/path_utils.dart @@ -0,0 +1,37 @@ +final RegExp _parameterRegExp = RegExp(r':(\w+)(\((?:\\.|[^\\()])+\))?'); + +RegExp patternToRegExp(String pattern, List parameters) { + final StringBuffer buffer = StringBuffer('^'); + int start = 0; + for (final RegExpMatch match in _parameterRegExp.allMatches(pattern)) { + if (match.start > start) { + buffer.write(RegExp.escape(pattern.substring(start, match.start))); + } + final String name = match[1]!; + final String? optionalPattern = match[2]; + final String regex = optionalPattern != null + ? _escapeGroup(optionalPattern, name) + : '(?<$name>[^/]+)'; + buffer.write(regex); + parameters.add(name); + start = match.end; + } + + if (start < pattern.length) { + buffer.write(RegExp.escape(pattern.substring(start))); + } + + if (!pattern.endsWith('/')) { + buffer.write(r'(?=/|$)'); + } + return RegExp(buffer.toString(), caseSensitive: false); +} + +String _escapeGroup(String group, [String? name]) { + final String escapedGroup = group.replaceFirstMapped( + RegExp(r'[:=!]'), (Match match) => '\\${match[0]}'); + if (name != null) { + return '(?<$name>$escapedGroup)'; + } + return escapedGroup; +} \ No newline at end of file diff --git a/lib/v11/app/navigation/views/app_navigation_rail.dart b/lib/v11/app/navigation/views/app_navigation_rail.dart index 29eec05..2ddb93a 100644 --- a/lib/v11/app/navigation/views/app_navigation_rail.dart +++ b/lib/v11/app/navigation/views/app_navigation_rail.dart @@ -1,5 +1,7 @@ import 'package:flutter/material.dart'; import 'package:iroute/components/components.dart'; +import 'package:provider/provider.dart'; +import '../../../pages/sort/provider/state.dart'; import '../router/app_router_delegate.dart'; import '../router/iroute_config.dart'; import '../router/routes.dart'; @@ -37,7 +39,7 @@ class _AppNavigationRailState extends State { tail: Padding( padding: const EdgeInsets.only(bottom: 6.0), child: Text( - 'V0.0.10', + 'V0.0.11', style: TextStyle(color: Colors.white, fontSize: 12), ), ), @@ -66,6 +68,12 @@ class _AppNavigationRailState extends State { router.changePath(path, keepAlive: true,recordHistory: true); return; } + if(index ==2){ + SortState state = SortStateScope.read(context); + String name = state.config.name; + router.changePath('/app/sort/$name',recordHistory: true); + return; + } if (index == 4) { router.changePath(path, style: RouteStyle.push,recordHistory: true); return; diff --git a/lib/v11/app/navigation/views/app_top_bar/app_top_bar.dart b/lib/v11/app/navigation/views/app_top_bar/app_top_bar.dart index 150ac6b..b806eb5 100644 --- a/lib/v11/app/navigation/views/app_top_bar/app_top_bar.dart +++ b/lib/v11/app/navigation/views/app_top_bar/app_top_bar.dart @@ -1,7 +1,9 @@ import 'package:flutter/material.dart'; import 'package:iroute/components/components.dart'; +import '../../../../pages/sort/provider/state.dart'; import '../../router/app_router_delegate.dart'; import '../../router/routes.dart'; +import '../../router/utils/path_utils.dart'; import '../../router/views/route_back_indicator.dart'; import 'app_router_editor.dart'; import 'history_view_icon.dart'; @@ -83,6 +85,13 @@ class _RouterIndicatorState extends State { void _onRouterChange() { setState(() {}); + if(router.path.startsWith('/app/sort/')){ + SortState state = SortStateScope.of(context); + String name = router.path.replaceAll('/app/sort/', ''); + if(name!='settings'){ + state.selectName(name); + } + } } List pathToBreadcrumbItems(String path) { @@ -97,7 +106,12 @@ class _RouterIndicatorState extends State { for (String segment in uri.pathSegments) { to += '/$segment'; - String label = kRouteLabelMap[to] ?? '未知路由'; + String label = ''; + if(to.startsWith('/app/sort/')){ + label = to.replaceAll('/app/sort/', ''); + }else{ + label = kRouteLabelMap[to] ?? '未知路由'; + } if(label.isNotEmpty){ result.add(BreadcrumbItem(to: to, label: label, active: to == distPath)); } diff --git a/lib/v11/pages/color/color_page.dart b/lib/v11/pages/color/color_page.dart index 644005e..60d18c6 100644 --- a/lib/v11/pages/color/color_page.dart +++ b/lib/v11/pages/color/color_page.dart @@ -48,9 +48,10 @@ class _ColorPageState extends State { } void _selectColor(Color color){ - // String value = color.value.toRadixString(16); - // router.path = '/color/detail?color=$value'; - router.changePath('/app/color/detail',extra: color); + String value = color.value.toRadixString(16); + String path = '/app/color/detail?color=$value'; + // router.changePath('/app/color/detail',extra: color); + router.changePath(path,recordHistory: true); } diff --git a/lib/v11/pages/sort/provider/state.dart b/lib/v11/pages/sort/provider/state.dart index 1e79b05..b695b83 100644 --- a/lib/v11/pages/sort/provider/state.dart +++ b/lib/v11/pages/sort/provider/state.dart @@ -98,4 +98,7 @@ class SortStateScope extends InheritedNotifier { static SortState of(BuildContext context) => context.dependOnInheritedWidgetOfExactType()!.notifier!; + + static SortState read(BuildContext context) => + context.getInheritedWidgetOfExactType()!.notifier!; } \ No newline at end of file diff --git a/lib/v11/pages/sort/views/sort_page/sort_page.dart b/lib/v11/pages/sort/views/sort_page/sort_page.dart index f865e77..56beba3 100644 --- a/lib/v11/pages/sort/views/sort_page/sort_page.dart +++ b/lib/v11/pages/sort/views/sort_page/sort_page.dart @@ -81,7 +81,7 @@ class SortRailPanel extends StatelessWidget { options: sortNameMap.values.toList(), onSelected: (name) { state.selectName(name); - router.changePath('/app/sort/player'); + router.changePath('/app/sort/${name}',recordHistory: true); }, ), ), diff --git a/lib/v9/app/navigation/router/iroute.dart b/lib/v9/app/navigation/router/iroute.dart index dffa34b..f7d245e 100644 --- a/lib/v9/app/navigation/router/iroute.dart +++ b/lib/v9/app/navigation/router/iroute.dart @@ -148,7 +148,6 @@ class CellIRoute extends IRouteNode { if (pageBuilder != null) { return pageBuilder!(context, config, child); } - print("======CellIRoute#createCellPage${config.pageKey}================="); return MaterialPage( child: child, key: config.pageKey, diff --git a/test/algorithm/02_link_list_add.dart b/test/algorithm/linked_list/02_link_list_add.dart similarity index 100% rename from test/algorithm/02_link_list_add.dart rename to test/algorithm/linked_list/02_link_list_add.dart diff --git a/test/algorithm/linked_list/206_reverse_list.dart b/test/algorithm/linked_list/206_reverse_list.dart new file mode 100644 index 0000000..8cd71ea --- /dev/null +++ b/test/algorithm/linked_list/206_reverse_list.dart @@ -0,0 +1,39 @@ + +void main(){ + /// 打印 1->9->9->4 + ListNode listNode = ListNode(1, ListNode(9, ListNode(9,ListNode(4)))); + print(listNode); + ListNode? result = listNode.reverseList(listNode); + print(result); +} + +class ListNode { + int val; + ListNode? next; + ListNode([this.val = 0, this.next]); + + @override + String toString(){ + ListNode? cur = this; + String result = ""; + while( cur!=null){ + result = result +cur.val.toString(); + cur = cur.next; + if(cur!=null){ + result +='->'; + } + } + return result; + } + + ListNode? reverseList(ListNode? head) { + if (head == null || head.next == null) return head; + ListNode? pre = reverseList(head.next); + head.next?.next = head; + head.next = null; + return pre; + } +} + + + diff --git a/test/parser/path_utils.dart b/test/parser/path_utils.dart new file mode 100644 index 0000000..a3cec6f --- /dev/null +++ b/test/parser/path_utils.dart @@ -0,0 +1,158 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +final RegExp _parameterRegExp = RegExp(r':(\w+)(\((?:\\.|[^\\()])+\))?'); + +/// Converts a [pattern] such as `/user/:id` into [RegExp]. +/// +/// The path parameters can be specified by prefixing them with `:`. The +/// `parameters` are used for storing path parameter names. +/// +/// +/// For example: +/// +/// `pattern` = `/user/:id/book/:bookId` +/// +/// The `parameters` would contain `['id', 'bookId']` as a result of calling +/// this method. +/// +/// To extract the path parameter values from a [RegExpMatch], pass the +/// [RegExpMatch] into [extractPathParameters] with the `parameters` that are +/// used for generating the [RegExp]. +RegExp patternToRegExp(String pattern, List parameters) { + final StringBuffer buffer = StringBuffer('^'); + int start = 0; + for (final RegExpMatch match in _parameterRegExp.allMatches(pattern)) { + if (match.start > start) { + buffer.write(RegExp.escape(pattern.substring(start, match.start))); + } + final String name = match[1]!; + final String? optionalPattern = match[2]; + final String regex = optionalPattern != null + ? _escapeGroup(optionalPattern, name) + : '(?<$name>[^/]+)'; + buffer.write(regex); + parameters.add(name); + start = match.end; + } + + if (start < pattern.length) { + buffer.write(RegExp.escape(pattern.substring(start))); + } + + if (!pattern.endsWith('/')) { + buffer.write(r'(?=/|$)'); + } + return RegExp(buffer.toString(), caseSensitive: false); +} + +String _escapeGroup(String group, [String? name]) { + final String escapedGroup = group.replaceFirstMapped( + RegExp(r'[:=!]'), (Match match) => '\\${match[0]}'); + if (name != null) { + return '(?<$name>$escapedGroup)'; + } + return escapedGroup; +} + +/// Reconstructs the full path from a [pattern] and path parameters. +/// +/// This is useful for restoring the original path from a [RegExpMatch]. +/// +/// For example, A path matched a [RegExp] returned from [patternToRegExp] and +/// produced a [RegExpMatch]. To reconstruct the path from the match, one +/// can follow these steps: +/// +/// 1. Get the `pathParameters` by calling [extractPathParameters] with the +/// [RegExpMatch] and the parameters used for generating the [RegExp]. +/// 2. Call [patternToPath] with the `pathParameters` from the first step and +/// the original `pattern` used for generating the [RegExp]. +String patternToPath(String pattern, Map pathParameters) { + final StringBuffer buffer = StringBuffer(); + int start = 0; + for (final RegExpMatch match in _parameterRegExp.allMatches(pattern)) { + if (match.start > start) { + buffer.write(pattern.substring(start, match.start)); + } + final String name = match[1]!; + buffer.write(pathParameters[name]); + start = match.end; + } + + if (start < pattern.length) { + buffer.write(pattern.substring(start)); + } + return buffer.toString(); +} + +/// Extracts arguments from the `match` and maps them by parameter name. +/// +/// The [parameters] should originate from the call to [patternToRegExp] that +/// creates the [RegExp]. +Map extractPathParameters( + List parameters, RegExpMatch match) { + return { + for (int i = 0; i < parameters.length; ++i) + parameters[i]: match.namedGroup(parameters[i])! + }; +} + +/// Concatenates two paths. +/// +/// e.g: pathA = /a, pathB = c/d, concatenatePaths(pathA, pathB) = /a/c/d. +String concatenatePaths(String parentPath, String childPath) { + // at the root, just return the path + if (parentPath.isEmpty) { + assert(childPath.startsWith('/')); + assert(childPath == '/' || !childPath.endsWith('/')); + return childPath; + } + + // not at the root, so append the parent path + assert(childPath.isNotEmpty); + assert(!childPath.startsWith('/')); + assert(!childPath.endsWith('/')); + return '${parentPath == '/' ? '' : parentPath}/$childPath'; +} + +/// Normalizes the location string. +String canonicalUri(String loc) { + String canon = Uri.parse(loc).toString(); + canon = canon.endsWith('?') ? canon.substring(0, canon.length - 1) : canon; + + // remove trailing slash except for when you shouldn't, e.g. + // /profile/ => /profile + // / => / + // /login?from=/ => login?from=/ + canon = canon.endsWith('/') && canon != '/' && !canon.contains('?') + ? canon.substring(0, canon.length - 1) + : canon; + + // /login/?from=/ => /login?from=/ + // /?from=/ => /?from=/ + canon = canon.replaceFirst('/?', '?', 1); + + return canon; +} + +/// Builds an absolute path for the provided route. +// String? fullPathForRoute( +// RouteBase targetRoute, String parentFullpath, List routes) { +// for (final RouteBase route in routes) { +// final String fullPath = (route is GoRoute) +// ? concatenatePaths(parentFullpath, route.path) +// : parentFullpath; +// +// if (route == targetRoute) { +// return fullPath; +// } else { +// final String? subRoutePath = +// fullPathForRoute(targetRoute, fullPath, route.routes); +// if (subRoutePath != null) { +// return subRoutePath; +// } +// } +// } +// return null; +// } diff --git a/test/parser/uri.dart b/test/parser/uri.dart new file mode 100644 index 0000000..70b081c --- /dev/null +++ b/test/parser/uri.dart @@ -0,0 +1,107 @@ +import 'path_utils.dart'; + +void main() { + String pattern = '/user/:id/book/:bookId'; + String path = '/user/001/book/card'; + + parserPath(); + // List params = []; + // RegExp regExp = patternToRegExp(pattern,params); + // + // + // bool data = regExp.hasMatch(path); + // List matchs = regExp.allMatches(path).toList(); + // Map pathParams = {}; + // if(matchs.isNotEmpty){ + // + // for(int i=0;i keys = []; + RegExp regExp = patternToRegExp(template,keys); + Map pathParams = {}; + RegExpMatch? match = regExp.firstMatch(path); + + if (match != null) { + for (int i = 0; i < keys.length; i++) { + String? value = match.namedGroup(keys[i]); + pathParams[keys[i]] = value; + } + } + print(pathParams); +} + + +final RegExp _parameterRegExp = RegExp(r':(\w+)(\((?:\\.|[^\\()])+\))?'); + +RegExp patternToRegExp(String pattern, List parameters) { + final StringBuffer buffer = StringBuffer('^'); + int start = 0; + for (final RegExpMatch match in _parameterRegExp.allMatches(pattern)) { + if (match.start > start) { + buffer.write(RegExp.escape(pattern.substring(start, match.start))); + } + final String name = match[1]!; + final String? optionalPattern = match[2]; + final String regex = optionalPattern != null + ? _escapeGroup(optionalPattern, name) + : '(?<$name>[^/]+)'; + buffer.write(regex); + parameters.add(name); + start = match.end; + } + + if (start < pattern.length) { + buffer.write(RegExp.escape(pattern.substring(start))); + } + + if (!pattern.endsWith('/')) { + buffer.write(r'(?=/|$)'); + } + return RegExp(buffer.toString(), caseSensitive: false); +} + +String _escapeGroup(String group, [String? name]) { + final String escapedGroup = group.replaceFirstMapped( + RegExp(r'[:=!]'), (Match match) => '\\${match[0]}'); + if (name != null) { + return '(?<$name>$escapedGroup)'; + } + return escapedGroup; +} + +void parser() { + String path = + 'http://user:pwd@toly1994.com:8080/path1/path2?key1=value1&key2=value2#fragment'; + Uri? uri = Uri.tryParse(path); + if (uri == null) return; + print(uri.scheme); // http + print(uri.userInfo); // user:pwd + print(uri.host); // toly1994.com + print(uri.port); // 8080 + print(uri.path); // /path1/path2 + print(uri.query); // key1=value1&key2=value2 + print(uri.fragment); // fragment +} diff --git a/test/structure/link_list/find.dart b/test/structure/link_list/find.dart new file mode 100644 index 0000000..9d15698 --- /dev/null +++ b/test/structure/link_list/find.dart @@ -0,0 +1,204 @@ +/// Dart、Rust 、C++ 、Java + +void main() { + ListNode listNode = ListNode(0, ListNode(3, ListNode(2, ListNode(8)))); + print(listNode); + + // List evens = listNode.getEvenValue(); + // print(evens); + + // List values = listNode.getValueByStep(2); + // print(values); + + // listNode.deleteAt(3); + // ListNode? ret = listNode.deleteFromEnd(2); + // print(ret); + + // ListNode? middleNode1 = listNode.middleNode1(); + // print(middleNode1); + + ListNode? middleNode2 = listNode.middleNode2(); + print(middleNode2); + // ListNode? findNode = listNode.find(8); + // if(findNode!=null){ + // ListNode? result = listNode.deleteNode(findNode); + // // 0->2->8 + // print(result); + // } +} + +class ListNode { + int val; + ListNode? next; + ListNode([this.val = 0, this.next]); + + //// region [2023.11.20] + @override + String toString() { + ListNode? cur = this; + String result = ""; + while (cur != null) { + result = result + cur.val.toString(); + cur = cur.next; + if (cur != null) { + result += '->'; + } + } + return result; + } + + // endregion + + //// region [2023.11.21] + /// [2023.11.21] TODO: 完成 find 函数,在链表中查找指定值的首位节点 + ListNode? find(int target) { + ListNode? cur = this; + while (cur != null) { + if (cur.val == target) { + return cur; + } + cur = cur.next; + } + return null; + } + + /// [2023.11.21] + /// TODO: LeetCode 237 删除指定节点(非尾结点) + void deleteNodeNotLast(ListNode node) { + if (node.next != null) { + node.val = node.next!.val; + node.next = node.next?.next; + } + } + + // endregion + + //// region [2023.11.22] + /// [2023.11.22] + /// TODO: 完成 size 方法,返回链表长度 + int size() { + int i = 0; + ListNode? cur = this; + while (cur != null) { + cur = cur.next; + i++; + } + return i; + } + + /// [2023.11.22] + /// TODO: 完成 deleteAt 方法,删除第 index 个节点 + void deleteAt(int index) { + if (index == 0) { + ListNode? next = this.next; + this.val = next?.val ?? -1; + this.next = next?.next; + next?.next = null; + return; + } + + ListNode? cur = this; + for (int i = 0; i < index - 1; i++) { + cur = cur?.next; + } + ListNode? target = cur?.next; + cur?.next = target?.next; + target?.next = null; + } + + /// [2023.11.22] + /// TODO: LeetCode 19 删除链表的倒数第 N 个节点 + ListNode? deleteFromEnd(int n) { + ListNode? dummy = ListNode(0, this); + ListNode? first = this; + ListNode? second = dummy; + for (int i = 0; i < n; ++i) { + first = first?.next; + } + while (first != null) { + first = first.next; + second = second?.next; + } + second?.next = second.next?.next; + return dummy.next; + } + + ListNode? deleteNode(ListNode node) { + ListNode? cur = this; + if (cur == node) { + cur = cur.next!; + return cur; + } + while (cur != null) { + if (cur.next == node) { + cur.next = cur.next?.next; + return this; + } + cur = cur.next; + } + return this; + } + + // endregion + + //// region [2023.11.23] + /// [2023.11.23] + /// TODO: 完成 getEvenValue 方法, + /// 返回链表中偶数节点的值列表 + List getEvenValue() { + ListNode? cur = this; + List result = []; + + while (cur != null) { + result.add(cur.val); + cur = cur.next?.next; + } + return result; + } + + /// TODO: 完成 getValueByStep 方法, + /// 返回链表中每隔 step 个节点的值列表 + /// 例: + /// 链表: 0->3->2->8->9 + /// step=3 : 输出 [0, 8] + /// step=2 : 输出 [0, 2, 9] + List getValueByStep(int step) { + ListNode? cur = this; + List result = []; + + while (cur != null) { + result.add(cur.val); + for (int i = 0; i < step; i++) { + cur = cur?.next; + } + } + return result; + } + + /// [2023.11.23] + /// TODO: LeetCode 876 链表的中间结点 + /// [1,2,3,4,5] -> [3,4,5] + /// [1,2,3,4,5,6] -> [4,5,6] + /// 时间 O(N) 空间 O(N) + ListNode? middleNode1() { + List nodes = []; + ListNode? cur = this; + while (cur != null) { + nodes.add(cur); + cur = cur.next; + } + return nodes[nodes.length ~/ 2]; + } + + /// 使用快慢指针 - 一次遍历 + ListNode? middleNode2() { + ListNode? slow = this; + ListNode? fast = this; + while (fast != null && fast.next != null) { + slow = slow?.next; + fast = fast.next?.next; + } + return slow; + } +// endregion +} diff --git a/test/structure/link_list/linked_list.dart b/test/structure/link_list/linked_list.dart new file mode 100644 index 0000000..c8f7b2a --- /dev/null +++ b/test/structure/link_list/linked_list.dart @@ -0,0 +1,3 @@ +class LikedList{ + +} \ No newline at end of file diff --git a/test/structure/link_list/print.dart b/test/structure/link_list/print.dart index a4ca869..16cf1a7 100644 --- a/test/structure/link_list/print.dart +++ b/test/structure/link_list/print.dart @@ -23,7 +23,4 @@ class ListNode { } return result; } - - - } \ No newline at end of file