├── 数据结构 ├── 集合和映射 │ └── 集合和映射.md ├── 二叉树 │ ├── 树.png │ ├── 二叉树.png │ ├── 树1.png │ ├── 树2.png │ ├── 树3.png │ ├── 树4.png │ ├── 树5.png │ ├── 中序遍历.png │ ├── 删除最小元素.png │ ├── 前序遍历.png │ ├── 后序遍历.png │ ├── 删除任意节点1.png │ ├── 删除任意节点2.png │ └── 二叉树 │ │ └── 二叉树 │ │ ├── main.swift │ │ └── BTS.swift ├── 递归 │ ├── 递归.png │ ├── 递归 │ │ └── 递归 │ │ │ ├── main.swift │ │ │ └── Recursion.swift │ └── 递归.md ├── 链表 │ ├── 节点.png │ ├── 虚拟头节点.png │ ├── 链表 │ │ └── 链表 │ │ │ ├── main.swift │ │ │ ├── LeetCode.swift │ │ │ └── LinkedList.swift │ └── 链表.md ├── 哈希表 │ ├── 拉链法.png │ ├── 哈希表大小1.png │ ├── 哈希表大小2.png │ ├── 哈希表大小3.png │ ├── 线性探测法.png │ └── 哈希表.md ├── 并查集 │ ├── 并查集.png │ ├── 并查集 │ │ └── 并查集 │ │ │ ├── main.swift │ │ │ ├── UnionFind1.swift │ │ │ ├── UnionFind2.swift │ │ │ ├── UnionFind3.swift │ │ │ └── UnionFind4.swift │ └── 并查集.md ├── 栈和队列 │ ├── 栈.png │ ├── 队列.png │ ├── 循环队列.png │ ├── 栈和队列 │ │ └── 栈和队列 │ │ │ ├── Queue.swift │ │ │ ├── main.swift │ │ │ ├── Solution.swift │ │ │ ├── Stack.swift │ │ │ └── LoopQueue.swift │ └── 栈和队列.md ├── Trie │ ├── Trie1.png │ ├── Trie2.png │ ├── Trie │ │ └── Trie │ │ │ ├── main.swift │ │ │ ├── leetCode211.swift │ │ │ └── TrieMap.swift │ └── Trie.md ├── 优先队列 │ ├── 二叉堆.png │ ├── 完全二叉树.png │ ├── 满二叉树.png │ ├── 算法效率.png │ ├── 线性结构.png │ ├── siftUp.png │ ├── siftDown.png │ ├── siftUp打印.png │ ├── 优先队列 │ │ └── 优先队列 │ │ │ ├── main.swift │ │ │ └── MaxHeap.swift │ └── 优先队列.md ├── 平衡二叉树 │ ├── AVL1.png │ ├── AVL2.png │ ├── AVL3.png │ ├── AVL4.png │ ├── AVL5.png │ ├── AVL6.png │ ├── AVL │ │ └── AVL │ │ │ ├── main.swift │ │ │ └── AVLTree.swift │ └── 平衡二叉树.md ├── 线段树 │ ├── 线段树1.png │ ├── 线段树2.png │ ├── 线段树 │ │ └── 线段树 │ │ │ ├── main.swift │ │ │ ├── Leetcode303.swift │ │ │ └── SegmentTree.swift │ └── 线段树.md └── 打印树 │ └── 如何直观形象地树状打印一棵二叉树?.md ├── sort.png ├── 排序 ├── 桶排序 │ ├── 桶排序.gif │ ├── 桶排序 │ │ └── 桶排序 │ │ │ ├── main.swift │ │ │ └── BucketSort.swift │ └── 桶排序.md ├── 基数排序 │ ├── 基数排序.png │ ├── radixSort.gif │ ├── 基数排序 │ │ └── 基数排序 │ │ │ ├── main.swift │ │ │ └── radixSort.swift │ └── 基数排序.md ├── 快速排序 │ ├── 快速排序.png │ ├── 打印结果.png │ ├── 优化1打印.png │ ├── 优化2打印.png │ ├── 快速排序优化1.png │ ├── 快速排序优化2.png │ ├── 快速排序 │ │ └── 快速排序 │ │ │ ├── main.swift │ │ │ └── quickSort.swift │ └── 快速排序.md ├── 计数排序 │ ├── 计数排序.gif │ ├── 计数排序 │ │ └── 计数排序 │ │ │ ├── main.swift │ │ │ └── CountingSort.swift │ └── 计数排序.md ├── 希尔排序 │ ├── 希尔排序1.jpeg │ ├── 希尔排序2.jpeg │ ├── 希尔排序3.jpeg │ ├── 希尔排序4.jpeg │ ├── 希尔排序5.jpeg │ ├── 希尔排序6.jpeg │ ├── 希尔排序7.jpeg │ ├── 希尔排序8.gif │ ├── 希尔排序 │ │ └── 希尔排序 │ │ │ ├── main.swift │ │ │ ├── SortTestHelper.swift │ │ │ └── ShellSort.swift │ └── 希尔排序.md ├── 归并排序 │ ├── 归并排序1.png │ ├── 归并排序2.png │ ├── 归并排序 │ │ └── 归并排序 │ │ │ ├── 归并排序-Bridging-Header.h │ │ │ ├── main.swift │ │ │ └── MergeSort.swift │ └── 归并排序.md ├── 简单排序算法 │ ├── 冒泡排序.png │ ├── 冒泡排序1.png │ ├── 插入排序.png │ ├── 选择排序.png │ ├── Sort │ │ └── Sort │ │ │ ├── main.swift │ │ │ ├── InsertionSort.swift │ │ │ ├── SelectionSort.swift │ │ │ └── BubbleSorting.swift │ └── 简单排序算法.md ├── 冒泡排序 │ ├── bubbleSort.gif │ ├── Sort │ │ └── Sort │ │ │ ├── main.swift │ │ │ ├── InsertionSort.swift │ │ │ ├── SelectionSort.swift │ │ │ └── BubbleSorting.swift │ └── 冒泡排序.md ├── 插入排序 │ ├── insertionSort.gif │ ├── Sort │ │ └── Sort │ │ │ ├── main.swift │ │ │ ├── InsertionSort.swift │ │ │ ├── SelectionSort.swift │ │ │ └── BubbleSorting.swift │ └── 插入排序.md ├── 选择排序 │ ├── selectionSort.gif │ ├── Sort │ │ └── Sort │ │ │ ├── main.swift │ │ │ ├── InsertionSort.swift │ │ │ ├── SelectionSort.swift │ │ │ └── BubbleSorting.swift │ └── 选择排序.md ├── 测试辅助 │ └── 测试辅助 │ │ ├── main.swift │ │ └── SortTestHelper.swift └── 堆排序 │ └── 堆排序.md ├── 算法 └── 算法 │ ├── 10大排序算法 │ ├── 03-Insertion(插入排序) │ │ └── InsertionSort.swift │ ├── 02-Selection(选择排序) │ │ └── SelectionSorting.swift │ ├── 01-Bubble(冒泡排序) │ │ └── BubbleSorting.swift │ └── 06-quick(快速排序) │ │ └── quickSort.swift │ ├── 面试题 │ ├── 15-LRU缓存机制 │ │ └── Solution15.swift │ ├── 17-用最少的硬币凑出m元 │ │ └── Solution17.swift │ ├── 08-数组中找出前4大的数字 │ │ └── Solution8.swift │ ├── 10-递归计算1~100的和 │ │ └── Solution10.swift │ ├── 09-白鼠试毒酒问题(二进制) │ │ └── Solution9.swift │ ├── 12-去除有序数组中重复的元素 │ │ └── Solution12.swift │ ├── 11-把一个十进制数转成二进制数,其中1的个数 │ │ └── Solution11.swift │ ├── 18-链表反转 │ │ └── Solution18.swift │ ├── 03-整数数组奇数在偶数前 │ │ └── Solution3.swift │ ├── 05-整数反转 │ │ └── Solution5.swift │ ├── 22-大数相加 │ │ └── Solution22.swift │ ├── 19-求平方根 │ │ └── Solution19.swift │ ├── 04-爬楼梯 │ │ └── Solution4.swift │ ├── 21-判断二叉树是否对称 │ │ └── Solution21.swift │ ├── 14-寻找单向链表的倒数第k个节点 │ │ └── Solution14.swift │ ├── 16-最大子序和 │ │ └── Solution16.swift │ ├── 20-判断链表是否有环 │ │ └── Solution20.swift │ ├── 13-合并有序数组 │ │ └── Solution13.swift │ ├── 06-红、黄、蓝三种颜色的气球 │ │ └── Solution6.swift │ ├── 02-链表是否为回文结构 │ │ └── Solution2.swift │ ├── 01-有效的括号 │ │ └── Solution1.swift │ └── 07-两个单向链表的第一个公共节点 │ │ └── Solution7.swift │ ├── main.swift │ ├── 数据结构 │ ├── 栈和队列 │ │ └── Stack.swift │ ├── 链表 │ │ ├── DoubleList.swift │ │ └── LinkedList.swift │ └── 二叉树 │ │ └── BTS.swift │ ├── Node.swift │ └── SortTestHelper.swift ├── .gitignore ├── 算法效率的度量.md ├── README.md └── README的副本.md /数据结构/集合和映射/集合和映射.md: -------------------------------------------------------------------------------- 1 | ## 集合和映射 2 | -------------------------------------------------------------------------------- /sort.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SunshineBrother/LeetCodeStudy/HEAD/sort.png -------------------------------------------------------------------------------- /排序/桶排序/桶排序.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SunshineBrother/LeetCodeStudy/HEAD/排序/桶排序/桶排序.gif -------------------------------------------------------------------------------- /数据结构/二叉树/树.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SunshineBrother/LeetCodeStudy/HEAD/数据结构/二叉树/树.png -------------------------------------------------------------------------------- /数据结构/递归/递归.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SunshineBrother/LeetCodeStudy/HEAD/数据结构/递归/递归.png -------------------------------------------------------------------------------- /数据结构/链表/节点.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SunshineBrother/LeetCodeStudy/HEAD/数据结构/链表/节点.png -------------------------------------------------------------------------------- /排序/基数排序/基数排序.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SunshineBrother/LeetCodeStudy/HEAD/排序/基数排序/基数排序.png -------------------------------------------------------------------------------- /排序/快速排序/快速排序.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SunshineBrother/LeetCodeStudy/HEAD/排序/快速排序/快速排序.png -------------------------------------------------------------------------------- /排序/快速排序/打印结果.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SunshineBrother/LeetCodeStudy/HEAD/排序/快速排序/打印结果.png -------------------------------------------------------------------------------- /排序/计数排序/计数排序.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SunshineBrother/LeetCodeStudy/HEAD/排序/计数排序/计数排序.gif -------------------------------------------------------------------------------- /数据结构/二叉树/二叉树.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SunshineBrother/LeetCodeStudy/HEAD/数据结构/二叉树/二叉树.png -------------------------------------------------------------------------------- /数据结构/二叉树/树1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SunshineBrother/LeetCodeStudy/HEAD/数据结构/二叉树/树1.png -------------------------------------------------------------------------------- /数据结构/二叉树/树2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SunshineBrother/LeetCodeStudy/HEAD/数据结构/二叉树/树2.png -------------------------------------------------------------------------------- /数据结构/二叉树/树3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SunshineBrother/LeetCodeStudy/HEAD/数据结构/二叉树/树3.png -------------------------------------------------------------------------------- /数据结构/二叉树/树4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SunshineBrother/LeetCodeStudy/HEAD/数据结构/二叉树/树4.png -------------------------------------------------------------------------------- /数据结构/二叉树/树5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SunshineBrother/LeetCodeStudy/HEAD/数据结构/二叉树/树5.png -------------------------------------------------------------------------------- /数据结构/哈希表/拉链法.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SunshineBrother/LeetCodeStudy/HEAD/数据结构/哈希表/拉链法.png -------------------------------------------------------------------------------- /数据结构/并查集/并查集.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SunshineBrother/LeetCodeStudy/HEAD/数据结构/并查集/并查集.png -------------------------------------------------------------------------------- /数据结构/栈和队列/栈.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SunshineBrother/LeetCodeStudy/HEAD/数据结构/栈和队列/栈.png -------------------------------------------------------------------------------- /数据结构/栈和队列/队列.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SunshineBrother/LeetCodeStudy/HEAD/数据结构/栈和队列/队列.png -------------------------------------------------------------------------------- /排序/希尔排序/希尔排序1.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SunshineBrother/LeetCodeStudy/HEAD/排序/希尔排序/希尔排序1.jpeg -------------------------------------------------------------------------------- /排序/希尔排序/希尔排序2.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SunshineBrother/LeetCodeStudy/HEAD/排序/希尔排序/希尔排序2.jpeg -------------------------------------------------------------------------------- /排序/希尔排序/希尔排序3.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SunshineBrother/LeetCodeStudy/HEAD/排序/希尔排序/希尔排序3.jpeg -------------------------------------------------------------------------------- /排序/希尔排序/希尔排序4.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SunshineBrother/LeetCodeStudy/HEAD/排序/希尔排序/希尔排序4.jpeg -------------------------------------------------------------------------------- /排序/希尔排序/希尔排序5.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SunshineBrother/LeetCodeStudy/HEAD/排序/希尔排序/希尔排序5.jpeg -------------------------------------------------------------------------------- /排序/希尔排序/希尔排序6.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SunshineBrother/LeetCodeStudy/HEAD/排序/希尔排序/希尔排序6.jpeg -------------------------------------------------------------------------------- /排序/希尔排序/希尔排序7.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SunshineBrother/LeetCodeStudy/HEAD/排序/希尔排序/希尔排序7.jpeg -------------------------------------------------------------------------------- /排序/希尔排序/希尔排序8.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SunshineBrother/LeetCodeStudy/HEAD/排序/希尔排序/希尔排序8.gif -------------------------------------------------------------------------------- /排序/归并排序/归并排序1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SunshineBrother/LeetCodeStudy/HEAD/排序/归并排序/归并排序1.png -------------------------------------------------------------------------------- /排序/归并排序/归并排序2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SunshineBrother/LeetCodeStudy/HEAD/排序/归并排序/归并排序2.png -------------------------------------------------------------------------------- /排序/快速排序/优化1打印.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SunshineBrother/LeetCodeStudy/HEAD/排序/快速排序/优化1打印.png -------------------------------------------------------------------------------- /排序/快速排序/优化2打印.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SunshineBrother/LeetCodeStudy/HEAD/排序/快速排序/优化2打印.png -------------------------------------------------------------------------------- /排序/快速排序/快速排序优化1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SunshineBrother/LeetCodeStudy/HEAD/排序/快速排序/快速排序优化1.png -------------------------------------------------------------------------------- /排序/快速排序/快速排序优化2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SunshineBrother/LeetCodeStudy/HEAD/排序/快速排序/快速排序优化2.png -------------------------------------------------------------------------------- /排序/简单排序算法/冒泡排序.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SunshineBrother/LeetCodeStudy/HEAD/排序/简单排序算法/冒泡排序.png -------------------------------------------------------------------------------- /排序/简单排序算法/冒泡排序1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SunshineBrother/LeetCodeStudy/HEAD/排序/简单排序算法/冒泡排序1.png -------------------------------------------------------------------------------- /排序/简单排序算法/插入排序.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SunshineBrother/LeetCodeStudy/HEAD/排序/简单排序算法/插入排序.png -------------------------------------------------------------------------------- /排序/简单排序算法/选择排序.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SunshineBrother/LeetCodeStudy/HEAD/排序/简单排序算法/选择排序.png -------------------------------------------------------------------------------- /数据结构/Trie/Trie1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SunshineBrother/LeetCodeStudy/HEAD/数据结构/Trie/Trie1.png -------------------------------------------------------------------------------- /数据结构/Trie/Trie2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SunshineBrother/LeetCodeStudy/HEAD/数据结构/Trie/Trie2.png -------------------------------------------------------------------------------- /数据结构/二叉树/中序遍历.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SunshineBrother/LeetCodeStudy/HEAD/数据结构/二叉树/中序遍历.png -------------------------------------------------------------------------------- /数据结构/二叉树/删除最小元素.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SunshineBrother/LeetCodeStudy/HEAD/数据结构/二叉树/删除最小元素.png -------------------------------------------------------------------------------- /数据结构/二叉树/前序遍历.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SunshineBrother/LeetCodeStudy/HEAD/数据结构/二叉树/前序遍历.png -------------------------------------------------------------------------------- /数据结构/二叉树/后序遍历.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SunshineBrother/LeetCodeStudy/HEAD/数据结构/二叉树/后序遍历.png -------------------------------------------------------------------------------- /数据结构/优先队列/二叉堆.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SunshineBrother/LeetCodeStudy/HEAD/数据结构/优先队列/二叉堆.png -------------------------------------------------------------------------------- /数据结构/优先队列/完全二叉树.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SunshineBrother/LeetCodeStudy/HEAD/数据结构/优先队列/完全二叉树.png -------------------------------------------------------------------------------- /数据结构/优先队列/满二叉树.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SunshineBrother/LeetCodeStudy/HEAD/数据结构/优先队列/满二叉树.png -------------------------------------------------------------------------------- /数据结构/优先队列/算法效率.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SunshineBrother/LeetCodeStudy/HEAD/数据结构/优先队列/算法效率.png -------------------------------------------------------------------------------- /数据结构/优先队列/线性结构.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SunshineBrother/LeetCodeStudy/HEAD/数据结构/优先队列/线性结构.png -------------------------------------------------------------------------------- /数据结构/哈希表/哈希表大小1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SunshineBrother/LeetCodeStudy/HEAD/数据结构/哈希表/哈希表大小1.png -------------------------------------------------------------------------------- /数据结构/哈希表/哈希表大小2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SunshineBrother/LeetCodeStudy/HEAD/数据结构/哈希表/哈希表大小2.png -------------------------------------------------------------------------------- /数据结构/哈希表/哈希表大小3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SunshineBrother/LeetCodeStudy/HEAD/数据结构/哈希表/哈希表大小3.png -------------------------------------------------------------------------------- /数据结构/哈希表/线性探测法.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SunshineBrother/LeetCodeStudy/HEAD/数据结构/哈希表/线性探测法.png -------------------------------------------------------------------------------- /数据结构/平衡二叉树/AVL1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SunshineBrother/LeetCodeStudy/HEAD/数据结构/平衡二叉树/AVL1.png -------------------------------------------------------------------------------- /数据结构/平衡二叉树/AVL2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SunshineBrother/LeetCodeStudy/HEAD/数据结构/平衡二叉树/AVL2.png -------------------------------------------------------------------------------- /数据结构/平衡二叉树/AVL3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SunshineBrother/LeetCodeStudy/HEAD/数据结构/平衡二叉树/AVL3.png -------------------------------------------------------------------------------- /数据结构/平衡二叉树/AVL4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SunshineBrother/LeetCodeStudy/HEAD/数据结构/平衡二叉树/AVL4.png -------------------------------------------------------------------------------- /数据结构/平衡二叉树/AVL5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SunshineBrother/LeetCodeStudy/HEAD/数据结构/平衡二叉树/AVL5.png -------------------------------------------------------------------------------- /数据结构/平衡二叉树/AVL6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SunshineBrother/LeetCodeStudy/HEAD/数据结构/平衡二叉树/AVL6.png -------------------------------------------------------------------------------- /数据结构/栈和队列/循环队列.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SunshineBrother/LeetCodeStudy/HEAD/数据结构/栈和队列/循环队列.png -------------------------------------------------------------------------------- /数据结构/线段树/线段树1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SunshineBrother/LeetCodeStudy/HEAD/数据结构/线段树/线段树1.png -------------------------------------------------------------------------------- /数据结构/线段树/线段树2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SunshineBrother/LeetCodeStudy/HEAD/数据结构/线段树/线段树2.png -------------------------------------------------------------------------------- /数据结构/链表/虚拟头节点.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SunshineBrother/LeetCodeStudy/HEAD/数据结构/链表/虚拟头节点.png -------------------------------------------------------------------------------- /排序/基数排序/radixSort.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SunshineBrother/LeetCodeStudy/HEAD/排序/基数排序/radixSort.gif -------------------------------------------------------------------------------- /数据结构/二叉树/删除任意节点1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SunshineBrother/LeetCodeStudy/HEAD/数据结构/二叉树/删除任意节点1.png -------------------------------------------------------------------------------- /数据结构/二叉树/删除任意节点2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SunshineBrother/LeetCodeStudy/HEAD/数据结构/二叉树/删除任意节点2.png -------------------------------------------------------------------------------- /数据结构/优先队列/siftUp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SunshineBrother/LeetCodeStudy/HEAD/数据结构/优先队列/siftUp.png -------------------------------------------------------------------------------- /排序/冒泡排序/bubbleSort.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SunshineBrother/LeetCodeStudy/HEAD/排序/冒泡排序/bubbleSort.gif -------------------------------------------------------------------------------- /数据结构/优先队列/siftDown.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SunshineBrother/LeetCodeStudy/HEAD/数据结构/优先队列/siftDown.png -------------------------------------------------------------------------------- /数据结构/优先队列/siftUp打印.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SunshineBrother/LeetCodeStudy/HEAD/数据结构/优先队列/siftUp打印.png -------------------------------------------------------------------------------- /排序/插入排序/insertionSort.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SunshineBrother/LeetCodeStudy/HEAD/排序/插入排序/insertionSort.gif -------------------------------------------------------------------------------- /排序/选择排序/selectionSort.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SunshineBrother/LeetCodeStudy/HEAD/排序/选择排序/selectionSort.gif -------------------------------------------------------------------------------- /排序/归并排序/归并排序/归并排序/归并排序-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | // 2 | // Use this file to import your target's public headers that you would like to expose to Swift. 3 | // 4 | 5 | -------------------------------------------------------------------------------- /数据结构/并查集/并查集/并查集/main.swift: -------------------------------------------------------------------------------- 1 | // 2 | // main.swift 3 | // 并查集 4 | // 5 | // Created by yunna on 2019/5/15. 6 | // Copyright © 2019年 yunna. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /算法/算法/10大排序算法/03-Insertion(插入排序)/InsertionSort.swift: -------------------------------------------------------------------------------- 1 | // 2 | // InsertionSort.swift 3 | // 算法 4 | // 5 | // Created by 青青 on 2021/8/7. 6 | // 7 | 8 | import Cocoa 9 | 10 | class InsertionSort: NSObject { 11 | 12 | } 13 | -------------------------------------------------------------------------------- /排序/测试辅助/测试辅助/main.swift: -------------------------------------------------------------------------------- 1 | // 2 | // main.swift 3 | // 测试辅助 4 | // 5 | // Created by yunna on 2019/6/5. 6 | // Copyright © 2019年 yunna. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | print("Hello, World!") 12 | 13 | -------------------------------------------------------------------------------- /算法/算法/面试题/15-LRU缓存机制/Solution15.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Solution15.swift 3 | // 算法 4 | // 5 | // Created by 青青 on 2021/8/8. 6 | // 7 | 8 | import Cocoa 9 | 10 | class Solution15: NSObject { 11 | 12 | 13 | 14 | 15 | } 16 | -------------------------------------------------------------------------------- /算法/算法/面试题/17-用最少的硬币凑出m元/Solution17.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Solution17.swift 3 | // 算法 4 | // 5 | // Created by 青青 on 2021/8/8. 6 | /* 7 | 你有1元、5元、7元三种硬币,求一种方法,用最少的硬币凑出m元;(m∈[100, 1000]) 8 | 9 | 10 | 11 | 12 | */ 13 | 14 | import Cocoa 15 | 16 | class Solution17: NSObject { 17 | 18 | } 19 | -------------------------------------------------------------------------------- /数据结构/线段树/线段树/线段树/main.swift: -------------------------------------------------------------------------------- 1 | // 2 | // main.swift 3 | // 线段树 4 | // 5 | // Created by yunna on 2019/5/9. 6 | // Copyright © 2019年 yunna. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | let arr = [-2, 0, 3, -5, 2, -1] 12 | let num = NumArray1(arr) 13 | print(num.sumRange(0, 5)) 14 | 15 | 16 | -------------------------------------------------------------------------------- /算法/算法/main.swift: -------------------------------------------------------------------------------- 1 | // 2 | // main.swift 3 | // 算法 4 | // 5 | // Created by 青青 on 2021/8/7. 6 | 7 | 8 | import Foundation 9 | 10 | print(Solution12().removeDuplicates(nums: [1,1,2,2,2,2,3,3,4,5])) 11 | 12 | let a = Solution22().bigPlus(a: "9", b: "99999999999999999999999999999999999999999999999999999999999994") 13 | print(a) 14 | -------------------------------------------------------------------------------- /算法/算法/面试题/08-数组中找出前4大的数字/Solution8.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Solution8.swift 3 | // 算法 4 | // 5 | // Created by 青青 on 2021/8/8. 6 | /* 7 | 算法题--大数据取最大前几个 8 | 9 | 先把10亿个整数对1000取模,存储到1000个文件中,然后对每一个文件进行内部排序(比如快速排序,从大到小排序),然后再对这1000个文件进行多路归并,取出前1万个最大的数即可。 10 | */ 11 | 12 | import Cocoa 13 | 14 | class Solution8: NSObject { 15 | 16 | } 17 | -------------------------------------------------------------------------------- /排序/桶排序/桶排序/桶排序/main.swift: -------------------------------------------------------------------------------- 1 | // 2 | // main.swift 3 | // 桶排序 4 | // 5 | // Created by yunna on 2019/6/28. 6 | // Copyright © 2019年 yunna. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | let arr = generateRandomArray(count: 11, rangL: 1, rangR: 9) 12 | print(arr) 13 | let res = BucketSort().sort(arr: arr) 14 | print(res) 15 | -------------------------------------------------------------------------------- /数据结构/平衡二叉树/AVL/AVL/main.swift: -------------------------------------------------------------------------------- 1 | // 2 | // main.swift 3 | // AVL 4 | // 5 | // Created by yunna on 2019/5/27. 6 | // Copyright © 2019年 yunna. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /排序/计数排序/计数排序/计数排序/main.swift: -------------------------------------------------------------------------------- 1 | // 2 | // main.swift 3 | // 计数排序 4 | // 5 | // Created by yunna on 2019/6/28. 6 | // Copyright © 2019年 yunna. All rights reserved. 7 | //[3,1,1,2,1,1] 8 | 9 | import Foundation 10 | 11 | let arr = generateRandomArray(count: 10, rangL: 1, rangR: 5) 12 | print(arr) 13 | let result = CountingSort().sort(arr: arr) 14 | print(result) 15 | 16 | -------------------------------------------------------------------------------- /排序/基数排序/基数排序/基数排序/main.swift: -------------------------------------------------------------------------------- 1 | // 2 | // main.swift 3 | // 基数排序 4 | // 5 | // Created by yunna on 2019/6/29. 6 | // Copyright © 2019年 yunna. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | //let arr:[Int] = [10,5,19,17,57,24,101,67,81] 12 | let arr = generateRandomArray(count: 50, rangL: 0, rangR: 100) 13 | print(arr) 14 | let res = radixSort().sort(arr: arr) 15 | print(res) 16 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | .DS_Store 3 | */build/* 4 | *.pbxuser 5 | !default.pbxuser 6 | *.mode1v3 7 | !default.mode1v3 8 | *.mode2v3 9 | !default.mode2v3 10 | *.perspectivev3 11 | !default.perspectivev3 12 | xcuserdata 13 | profile 14 | *.moved-aside 15 | DerivedData 16 | .idea/ 17 | *.hmap 18 | *.xccheckout 19 | *.xcworkspace 20 | !default.xcworkspace 21 | 22 | #CocoaPods 23 | Pods 24 | !Podfile 25 | !Podfile.lock -------------------------------------------------------------------------------- /排序/堆排序/堆排序.md: -------------------------------------------------------------------------------- 1 | # 堆排序(Heapsort) 2 | 3 | 4 | 堆排序(Heapsort)是指利用堆这种数据结构所设计的一种排序算法。堆积是一个近似完全二叉树的结构,并同时满足堆积的性质:即子结点的键值或索引总是小于(或者大于)它的父节点。堆排序可以说是一种利用堆的概念来排序的选择排序。分为两种方法: 5 | - 大顶堆:每个节点的值都大于或等于其子节点的值,在堆排序算法中用于升序排列; 6 | - 小顶堆:每个节点的值都小于或等于其子节点的值,在堆排序算法中用于降序排列; 7 | 8 | 堆排序的平均时间复杂度为 Ο(nlogn)。 9 | 10 | 11 | 12 | 13 | 其实就是我们的二分搜索树,具体可以参考数据结构中的[5、二叉树](https://github.com/SunshineBrother/LeetCodeStudy/blob/master/数据结构/二叉树/二叉树.md) 14 | 15 | 16 | -------------------------------------------------------------------------------- /排序/插入排序/Sort/Sort/main.swift: -------------------------------------------------------------------------------- 1 | // 2 | // main.swift 3 | // Sort 4 | // 5 | // Created by yunna on 2019/6/3. 6 | // Copyright © 2019年 yunna. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | 12 | let arr = [5, 4, 6, 3, 2, 1] 13 | //let bubble = BubbleSorting().sorting1(arr: arr) 14 | //let insert = InsertionSort().sorting(arr: arr) 15 | let select = SelectionSort().sorting(arr: arr) 16 | print(select) 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /排序/选择排序/Sort/Sort/main.swift: -------------------------------------------------------------------------------- 1 | // 2 | // main.swift 3 | // Sort 4 | // 5 | // Created by yunna on 2019/6/3. 6 | // Copyright © 2019年 yunna. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | 12 | let arr = [5, 4, 6, 3, 2, 1] 13 | //let bubble = BubbleSorting().sorting1(arr: arr) 14 | //let insert = InsertionSort().sorting(arr: arr) 15 | let select = SelectionSort().sorting(arr: arr) 16 | print(select) 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /排序/冒泡排序/Sort/Sort/main.swift: -------------------------------------------------------------------------------- 1 | // 2 | // main.swift 3 | // Sort 4 | // 5 | // Created by yunna on 2019/6/3. 6 | // Copyright © 2019年 yunna. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | 12 | let arr = [5, 4, 6, 3, 2, 1] 13 | //let bubble = BubbleSorting().sorting1(arr: arr) 14 | let insert = InsertionSort().sorting(arr: arr) 15 | //let select = SelectionSort().sorting(arr: arr) 16 | //print(select) 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /排序/简单排序算法/Sort/Sort/main.swift: -------------------------------------------------------------------------------- 1 | // 2 | // main.swift 3 | // Sort 4 | // 5 | // Created by yunna on 2019/6/3. 6 | // Copyright © 2019年 yunna. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | 12 | let arr = [5, 4, 6, 3, 2, 1] 13 | //let bubble = BubbleSorting().sorting1(arr: arr) 14 | //let insert = InsertionSort().sorting(arr: arr) 15 | let select = SelectionSort().sorting(arr: arr) 16 | print(select) 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /数据结构/优先队列/优先队列/优先队列/main.swift: -------------------------------------------------------------------------------- 1 | // 2 | // main.swift 3 | // 优先队列 4 | // 5 | // Created by yunna on 2019/5/6. 6 | // Copyright © 2019年 yunna. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | let heap = MaxHeap() 12 | let data = [62,41,30,28,16,22,13,19,17,15,52] 13 | for (_,item) in data.enumerated(){ 14 | heap.add(child: item) 15 | } 16 | print(heap.data) 17 | //删除 18 | let _ = heap.remove() 19 | print(heap.data) 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /算法/算法/数据结构/栈和队列/Stack.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Stack.swift 3 | // 算法 4 | // 5 | // Created by 青青 on 2021/8/7. 6 | // 7 | 8 | import Cocoa 9 | 10 | class Stack: NSObject { 11 | var stackArr = [Any]() 12 | //压栈 13 | func push(e: Any) { 14 | stackArr.append(e) 15 | } 16 | // 出栈 17 | func pop() -> Any? { 18 | guard stackArr.isEmpty else { 19 | return nil 20 | } 21 | return stackArr[stackArr.count - 1] 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /数据结构/二叉树/二叉树/二叉树/main.swift: -------------------------------------------------------------------------------- 1 | // 2 | // main.swift 3 | // 二叉树 4 | // 5 | // Created by yunna on 2019/4/1. 6 | // Copyright © 2019年 yunna. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | ///////////////// 12 | // 5 // 13 | // / \ // 14 | // 3 6 // 15 | // / \ \ // 16 | // 2 4 8 // 17 | ///////////////// 18 | 19 | let arr = [5,3,6,4,2,8] 20 | let bts = BTS() 21 | for item in arr{ 22 | bts.add(E: item) 23 | } 24 | 25 | bts.preOrder() 26 | 27 | -------------------------------------------------------------------------------- /数据结构/递归/递归/递归/main.swift: -------------------------------------------------------------------------------- 1 | // 2 | // main.swift 3 | // 递归 4 | // 5 | // Created by yunna on 2019/3/27. 6 | // Copyright © 2019年 yunna. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | var arr = [Int]() 12 | var count = 0 13 | for index in 0..<101{ 14 | arr.append(index) 15 | count += index; 16 | } 17 | print(count) 18 | let res = Recursion().add(arr: arr) 19 | print("递归算法:",res) 20 | 21 | //===递归链表=== 22 | let link = ListNode() 23 | link.listNode(arr: [1,2,3,4,5,6,7]) 24 | link.toString() 25 | 26 | -------------------------------------------------------------------------------- /算法/算法/面试题/10-递归计算1~100的和/Solution10.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Solution10.swift 3 | // 算法 4 | // 5 | // Created by 青青 on 2021/8/8. 6 | /* 7 | 递归计算1~100的和 8 | 9 | 1、找出临界值,无需计算就能够得出的值。 10 | 2、找这一次和上一次的关系 11 | 3、假设这个函数已经写好,写出第n次和第n-1次的关系公式。 12 | sum(100) = sum(99) + 100; 13 | sum(n) = sum(n - 1) + n; 14 | */ 15 | 16 | import Cocoa 17 | 18 | class Solution10: NSObject { 19 | func sum(n: Int) -> Int { 20 | if n == 1 { 21 | return 1 22 | } 23 | return sum(n: n-1) + n 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /数据结构/链表/链表/链表/main.swift: -------------------------------------------------------------------------------- 1 | // 2 | // main.swift 3 | // 链表 4 | // 5 | // Created by yunna on 2019/3/23. 6 | // Copyright © 2019年 yunna. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | var link = LinkedList() 11 | for index in 0..<15{ 12 | link.addLast(E: "\(index)") 13 | } 14 | link.toString() 15 | link.delete(index: 3) 16 | print("删除元素") 17 | link.toString() 18 | print("添加元素") 19 | link.add(index: 7, E: "7777") 20 | link.toString() 21 | print("修改元素") 22 | link.set(index: 10, E: "10086") 23 | link.toString() 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /数据结构/Trie/Trie/Trie/main.swift: -------------------------------------------------------------------------------- 1 | // 2 | // main.swift 3 | // Trie 4 | // 5 | // Created by yunna on 2019/5/14. 6 | // Copyright © 2019年 yunna. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | 12 | 13 | 14 | let add = ["WordDictionary","addWord","addWord","addWord","search","search","search","search"] 15 | let searchs = ["","bad","dad","mad","pad","bad",".ad","b.."] 16 | 17 | let Map = leetCode211() 18 | for item in add{ 19 | Map.addWord(item) 20 | } 21 | 22 | for item in searchs{ 23 | let res = Map.search(item) 24 | print(res) 25 | } 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /数据结构/递归/递归/递归/Recursion.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Recursion.swift 3 | // 递归 4 | // 5 | // Created by yunna on 2019/3/27. 6 | // Copyright © 2019年 yunna. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | class Recursion: NSObject { 12 | //数组求和 13 | func add(arr:[Int]) -> Int { 14 | return sum(arr: arr, l: 0) 15 | } 16 | //更小的问题 17 | private 18 | func sum(arr:[Int],l:Int) -> Int { 19 | if l == arr.count { 20 | return 0 21 | } 22 | let result = arr[l] + sum(arr: arr, l: l+1) 23 | return result 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /算法/算法/面试题/09-白鼠试毒酒问题(二进制)/Solution9.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Solution9.swift 3 | // 算法 4 | // 5 | // Created by 青青 on 2021/8/8. 6 | /* 7 | 题目:1000 瓶无色无味的药水,其中有一瓶毒药,10只小白鼠拿过来做实验。喝了无毒的药水第二天没事儿,喝了有毒的药水后第二天会死亡。如何在一天之内(第二天)找出这瓶有毒的药水? 8 | 9 | 思路就是用二进制,2^10=1024,也就是10只小白鼠最多能验出1024瓶药水,哪个有毒。小白鼠编号,1-10。瓶子也编号,1-1000,然后把瓶子的编号转变为二进制数。如果第几位是1,就把这瓶水给第几个小白鼠喝。最后大概每个小白鼠喝500瓶药水的混合液。如果还不懂,下面列几个数字解释一下。 10 | 11 | 12 | 大概就是这意思,再反过来,假如1号和3号小白鼠死了,死的小白鼠用1表示,再写成2进制数:0000000101,转化为十进制数是5,从上面列出来的也可以看出1,3都喝了5号瓶的水,所以就是第五瓶水有毒。 13 | 14 | */ 15 | 16 | import Cocoa 17 | 18 | class Solution9: NSObject { 19 | 20 | } 21 | -------------------------------------------------------------------------------- /排序/插入排序/Sort/Sort/InsertionSort.swift: -------------------------------------------------------------------------------- 1 | // 2 | // InsertionSort.swift 3 | // Sort 4 | // 5 | // Created by yunna on 2019/6/3. 6 | // Copyright © 2019年 yunna. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | class InsertionSort: NSObject { 12 | func sorting(arr:[Int]) -> Array{ 13 | var sortArr = arr 14 | for i in 0.. Array{ 13 | var sortArr = arr 14 | for i in 0.. Array{ 13 | var sortArr = arr 14 | for i in 0.. [Int] { 14 | var data = nums 15 | var slow = 0 16 | var fast = 0 17 | while fast < data.count - 1 { 18 | fast += 1 19 | slow = fast - 1 20 | if data[slow] == data[fast] { 21 | data.remove(at: fast) 22 | fast = slow 23 | } 24 | } 25 | return data 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /排序/冒泡排序/Sort/Sort/InsertionSort.swift: -------------------------------------------------------------------------------- 1 | // 2 | // InsertionSort.swift 3 | // Sort 4 | // 5 | // Created by yunna on 2019/6/3. 6 | // Copyright © 2019年 yunna. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | class InsertionSort: NSObject { 12 | func sorting(arr:[Int]) -> Array{ 13 | var sortArr = arr 14 | for i in 0.. Array{ 13 | var sortArr = arr 14 | for i in 0.. Array{ 13 | var sortArr = arr 14 | let n = sortArr.count 15 | for i in 0.. sortArr[j]{ 20 | minIndex = j 21 | } 22 | } 23 | sortArr.swapAt(i, minIndex) 24 | } 25 | return sortArr 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /算法/算法/面试题/11-把一个十进制数转成二进制数,其中1的个数/Solution11.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Solution11.swift 3 | // 算法 4 | // 5 | // Created by 青青 on 2021/8/8. 6 | /* 7 | 把一个十进制数转成二进制数,其中1的个数 8 | 9 | 得到10的二进制数为1010 10 | 那么我们会发现,10%2是判断二进制数的最后一位是0还是1,判断完成后向右移一位即10/2得到5,接着5%2判断二进制数的倒数第二位是0还是1,判断完成后向右移一位即5/2得2,重复这个过程,直到0/2结束。最终我们得到了10的二进制数1010. 11 | 12 | 根据上述思想,我们可以得到一个最初步的算法。 13 | 14 | */ 15 | 16 | import Cocoa 17 | 18 | class Solution11: NSObject { 19 | func solve(n:Int) -> Int { 20 | var result = 0 21 | var data = n 22 | while data / 2 != 0 { 23 | if data % 2 == 0 { 24 | result += 1 25 | } 26 | data = data / 2 27 | } 28 | 29 | return result 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /算法/算法/面试题/18-链表反转/Solution18.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Solution18.swift 3 | // 算法 4 | // 5 | // Created by 青青 on 2021/8/8. 6 | // 7 | 8 | import Cocoa 9 | 10 | class Solution18: NSObject { 11 | func reverseList(node: Node?) -> Node? { 12 | if node == nil { 13 | return nil 14 | } 15 | 16 | var nowNode: Node? = node 17 | var preNode: Node? = nil 18 | while nowNode != nil { 19 | // 暂存 20 | let nextNode: Node? = nowNode?.right 21 | nowNode?.right = preNode 22 | 23 | // 为下次循环做准备 24 | preNode = nowNode 25 | nowNode = nextNode 26 | } 27 | 28 | return preNode 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /排序/希尔排序/希尔排序/希尔排序/main.swift: -------------------------------------------------------------------------------- 1 | // 2 | // main.swift 3 | // 希尔排序 4 | // 5 | // Created by yunna on 2019/6/26. 6 | // Copyright © 2019年 yunna. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | let arr:[Int] = generateRandomArray(count: 10000, rangL: 0, rangR: 5000) 12 | 13 | let starttime1 = Date().timeIntervalSince1970 14 | let sort = ShellSort().shellSort(data: arr) 15 | let endTime1 = Date().timeIntervalSince1970 16 | 17 | let starttime2 = Date().timeIntervalSince1970 18 | let sort2 = InsertionSort().sorting(arr: arr) 19 | let endTime2 = Date().timeIntervalSince1970 20 | 21 | 22 | 23 | print("希尔排序:\(endTime1 - starttime1)") 24 | print("插入排序:\(endTime2 - starttime2)") 25 | 26 | /* 27 | 希尔排序:3.7632460594177246 28 | 插入排序:4.0005528926849365 29 | */ 30 | -------------------------------------------------------------------------------- /排序/快速排序/快速排序/快速排序/main.swift: -------------------------------------------------------------------------------- 1 | // 2 | // main.swift 3 | // 快速排序 4 | // 5 | // Created by yunna on 2019/6/22. 6 | // Copyright © 2019年 yunna. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | var arr:[Int] = [] 12 | for i in 0..<10000{ 13 | arr.append(i) 14 | } 15 | 16 | let starttime1 = Date().timeIntervalSince1970 17 | let res = quickSort().quickSort(arr: arr) 18 | let endTime1 = Date().timeIntervalSince1970 19 | 20 | 21 | 22 | let arr1 = generateRandomArray(count: 10000, rangL: 0, rangR: 10) 23 | let starttime2 = Date().timeIntervalSince1970 24 | let _ = quickSort().quickSort(arr: arr1) 25 | let endTime2 = Date().timeIntervalSince1970 26 | 27 | 28 | 29 | print("顺序数组快速排序:\(endTime1 - starttime1)") 30 | print("随机数组快速排序:\(endTime2 - starttime2)") 31 | -------------------------------------------------------------------------------- /算法/算法/10大排序算法/02-Selection(选择排序)/SelectionSorting.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SelectionSorting.swift 3 | // 算法 4 | // 5 | // Created by 青青 on 2021/8/7. 6 | //选择排序也会把数组分为已排序区和未排序区。但是与插入排序不同的是,它每次找到未排序区的最小值,与未排序区的首个元素交换,这样就变成了已排序区的末尾元素了 7 | 8 | import Cocoa 9 | class SelectionSorting: NSObject { 10 | func sorting(arr: [Int]) -> Array { 11 | var sortArr = arr 12 | for i in 0.. sortArr[j] { 17 | minIndex = j 18 | } 19 | } 20 | sortArr.swapAt(i, minIndex) 21 | } 22 | return sortArr 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /排序/选择排序/选择排序.md: -------------------------------------------------------------------------------- 1 | # 选择排序(Selection Sort) 2 | 3 | 4 | 选择排序也会把数组分为已排序区和未排序区。但是与插入排序不同的是,它每次找到未排序区的最小值,与未排序区的首个元素交换,这样就变成了已排序区的末尾元素了 5 | 6 | ![](https://github.com/SunshineBrother/LeetCodeStudy/tree/master/算法/选择排序/selectionSort.gif) 7 | 8 | ![](https://github.com/SunshineBrother/LeetCodeStudy/blob/master/算法/简单排序算法/选择排序.png) 9 | 10 | ``` 11 | func sorting(arr:[Int]) -> Array{ 12 | var sortArr = arr 13 | let n = sortArr.count 14 | for i in 0.. sortArr[j]{ 19 | minIndex = j 20 | } 21 | } 22 | 23 | sortArr.swapAt(i, minIndex) 24 | 25 | } 26 | 27 | 28 | return sortArr 29 | } 30 | ``` 31 | 32 | 33 | -------------------------------------------------------------------------------- /排序/插入排序/Sort/Sort/SelectionSort.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SelectionSort.swift 3 | // Sort 4 | // 5 | // Created by yunna on 2019/6/3. 6 | // Copyright © 2019年 yunna. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | class SelectionSort: NSObject { 12 | func sorting(arr:[Int]) -> Array{ 13 | var sortArr = arr 14 | let n = sortArr.count 15 | for i in 0.. sortArr[j]{ 20 | minIndex = j 21 | } 22 | } 23 | 24 | sortArr.swapAt(i, minIndex) 25 | 26 | } 27 | 28 | 29 | return sortArr 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /排序/选择排序/Sort/Sort/SelectionSort.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SelectionSort.swift 3 | // Sort 4 | // 5 | // Created by yunna on 2019/6/3. 6 | // Copyright © 2019年 yunna. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | class SelectionSort: NSObject { 12 | func sorting(arr:[Int]) -> Array{ 13 | var sortArr = arr 14 | let n = sortArr.count 15 | for i in 0.. sortArr[j]{ 20 | minIndex = j 21 | } 22 | } 23 | 24 | sortArr.swapAt(i, minIndex) 25 | 26 | } 27 | 28 | 29 | return sortArr 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /排序/简单排序算法/Sort/Sort/SelectionSort.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SelectionSort.swift 3 | // Sort 4 | // 5 | // Created by yunna on 2019/6/3. 6 | // Copyright © 2019年 yunna. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | class SelectionSort: NSObject { 12 | func sorting(arr:[Int]) -> Array{ 13 | var sortArr = arr 14 | let n = sortArr.count 15 | for i in 0.. sortArr[j]{ 20 | minIndex = j 21 | } 22 | } 23 | 24 | sortArr.swapAt(i, minIndex) 25 | 26 | } 27 | 28 | 29 | return sortArr 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /算法/算法/面试题/03-整数数组奇数在偶数前/Solution3.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Solution3.swift 3 | // 算法 4 | // 5 | // Created by 青青 on 2021/8/8. 6 | /* 7 | 有一个整数数组,如何只遍历一遍就实现让该数组奇数都在前面,偶数都在后面? 8 | 9 | 输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有的奇数位于数组的前半部分,所有的偶数位于位于数组的后半部分,并保证奇数和奇数,偶数和偶数之间的相对位置不变 10 | 11 | 12 | 冒泡算法思想,当前面数字为偶数,后面数字为奇数时,相互交换,否则不交换 13 | */ 14 | 15 | import Cocoa 16 | 17 | class Solution3: NSObject { 18 | 19 | func reOrderArray(data: [Int]) -> Array { 20 | var list = data 21 | for i in 0.. Int { 39 | var a = x 40 | var b = 0 41 | while a != 0 { 42 | b = b*10 + a%10 43 | a = a/10 44 | } 45 | return b 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /算法/算法/Node.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Node.swift 3 | // 算法 4 | // 5 | // Created by 青青 on 2021/8/7. 6 | // 7 | 8 | import Foundation 9 | 10 | class Node: NSObject { 11 | var key:String = "" 12 | var e: E! 13 | var left:Node? 14 | var right:Node? 15 | override init() { 16 | super.init() 17 | } 18 | init(e: E) { 19 | super.init() 20 | self.e = e 21 | } 22 | init(e: E, left: Node?, right: Node?) { 23 | super.init() 24 | self.e = e 25 | self.left = left 26 | self.right = right 27 | } 28 | init(e: E, right: Node?) { 29 | super.init() 30 | self.e = e 31 | self.right = right 32 | } 33 | 34 | 35 | init(key: String, e: E) { 36 | super.init() 37 | self.key = key 38 | self.e = e 39 | } 40 | 41 | 42 | } 43 | -------------------------------------------------------------------------------- /数据结构/递归/递归.md: -------------------------------------------------------------------------------- 1 | ## 递归 2 | 程序调用自身的编程技巧称为递归( recursion) 3 | 4 | 本质上,就是将原来的问题,转化为更小的同一个问题 5 | 6 | **举例:数组求和** 7 | ``` 8 | Sum(arr[0..n]) = arr[0] + Sum(arr[1..n]) <---更小的同一个问题 9 | Sum(arr[1..n]) = arr[1] + Sum(arr[2..n]) <---更小的同一个问题 10 | Sum(arr[2..n]) = arr[2] + Sum(arr[3..n]) <---更小的同一个问题 11 | ... 12 | Sum(arr[n-1..n]) = arr[n-1] + Sum(arr[]) 13 | ``` 14 | 我们来使用代码实现一下 15 | 16 | ``` 17 | class Recursion: NSObject { 18 | //数组求和 19 | func add(arr:[Int]) -> Int { 20 | return sum(arr: arr, l: 0) 21 | } 22 | //更小的问题 23 | private 24 | func sum(arr:[Int],l:Int) -> Int { 25 | if l == arr.count { 26 | return 0 27 | } 28 | let result = arr[l] + sum(arr: arr, l: l+1) 29 | return result 30 | } 31 | } 32 | 33 | ``` 34 | 35 | 其中,链表是具有天然的递归型的 36 | 37 | ![递归](https://github.com/SunshineBrother/LeetCodeStudy/blob/master/数据结构/递归/递归.png) 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /算法/算法/面试题/22-大数相加/Solution22.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Solution22.swift 3 | // 算法 4 | // 5 | // Created by 青青 on 2021/8/8. 6 | // 7 | 8 | import Cocoa 9 | 10 | class Solution22: NSObject { 11 | func bigPlus(a: String, b: String) -> String { 12 | let aList: [Character] = a.map{ $0 } 13 | let bList: [Character] = b.map{ $0 } 14 | var result = "" 15 | var i = aList.count - 1 16 | var j = bList.count - 1 17 | var flag = 0 18 | 19 | while i >= 0 || j >= 0 || flag != 0 { 20 | let x = i < 0 ? 0 : Int("\(aList[i])") ?? 0 21 | let y = j < 0 ? 0 : Int("\(bList[j])") ?? 0 22 | let sum = x + y + flag 23 | //添加到字符串尾部 24 | result = "\(sum % 10)" + result 25 | flag = sum / 10 26 | 27 | i -= 1 28 | j -= 1 29 | } 30 | 31 | return result 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /算法/算法/SortTestHelper.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SortTestHelper.swift 3 | // 算法 4 | // 5 | // Created by 青青 on 2021/8/7. 6 | // 7 | 8 | import Foundation 9 | import Cocoa 10 | 11 | // 生成有n个元素的随机数组,每个元素的随机范围为[rangeL, rangeR] 12 | func generateRandomArray(count:Int,rangL:Int,rangR:Int) -> Array { 13 | 14 | if rangR <= rangL{ 15 | fatalError("取值范围不准确") 16 | } 17 | 18 | var arr:[Int] = Array() 19 | for _ in 0.. Bool { 32 | for i in 0.. arr[i+1] { 34 | return false 35 | } 36 | } 37 | return true 38 | } 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /算法/算法/面试题/19-求平方根/Solution19.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Solution19.swift 3 | // 算法 4 | // 5 | // Created by 青青 on 2021/8/8. 6 | /* 7 | 实现函数 int sqrt(int x). 8 | 9 | 计算并返回x的平方根(向下取整) 10 | 二分查找 11 | 1.初始范围为1,x 12 | 2.当middle*middle <= x && (middle+1)*(middle+1) > x时,返回结果 13 | 3.当middle*middle < x时,到右半部分继续寻找 14 | 4.当middle*middle > x时,到左半部分继续寻找 15 | 16 | */ 17 | 18 | import Cocoa 19 | 20 | class Solution19: NSObject { 21 | func sqrt (_ x: Int) -> Int { 22 | if(x <= 0) { 23 | return 0 24 | } 25 | var left = 1 26 | var right = x 27 | while(true){ 28 | let middle = (left + right) / 2 29 | if (middle <= x / middle) && (middle + 1) > x / (middle + 1) { 30 | return middle 31 | }else if (middle < x / middle){ 32 | left = middle + 1 33 | }else{ 34 | right = middle - 1 35 | } 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /算法/算法/面试题/04-爬楼梯/Solution4.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Solution4.swift 3 | // 算法 4 | // 5 | // Created by 青青 on 2021/8/8. 6 | /* 7 | 假设你正在爬楼梯。需要 n 阶你才能到达楼顶。每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢? 8 | 9 | 假设你正在爬楼梯。需要 n 阶你才能到达楼顶。 10 | 每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢? 11 | 注意:给定 n 是一个正整数。 12 | 示例 1: 13 | 输入: 2 14 | 输出: 2 15 | 解释: 有两种方法可以爬到楼顶。 16 | 1. 1 阶 + 1 阶 17 | 2. 2 阶 18 | 19 | 示例 2: 20 | 输入: 3 21 | 输出: 3 22 | 解释: 有三种方法可以爬到楼顶。 23 | 1. 1 阶 + 1 阶 + 1 阶 24 | 2. 1 阶 + 2 阶 25 | 3. 2 阶 + 1 阶 26 | 27 | 这个题本质就是解裴波拉切数 28 | 定义F(n)表示到达第n个台阶的方法,则F(n) = F(n - 1) +F(n - 2) ; 29 | 思路清晰后代码如下: 30 | */ 31 | 32 | import Cocoa 33 | 34 | class Solution4: NSObject { 35 | func climbStairs(n:Int) -> Int { 36 | if n <= 0 { 37 | return 0 38 | } else if n == 1 { 39 | return 1 40 | } else if n == 2 { 41 | return 2 42 | } 43 | return climbStairs(n: n - 1) + climbStairs(n: n - 2) 44 | } 45 | } 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /排序/测试辅助/测试辅助/SortTestHelper.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SortTestHelper.swift 3 | // 测试辅助 4 | // 5 | // Created by yunna on 2019/6/5. 6 | // Copyright © 2019年 yunna. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | // 生成有n个元素的随机数组,每个元素的随机范围为[rangeL, rangeR] 12 | func generateRandomArray(count:Int,rangL:Int,rangR:Int) -> Array { 13 | 14 | if rangR <= rangL{ 15 | fatalError("取值范围不准确") 16 | } 17 | 18 | var arr:[Int] = Array() 19 | for _ in 0.. Bool { 32 | for i in 0.. arr[i+1] { 34 | return false 35 | } 36 | } 37 | return true 38 | } 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /排序/希尔排序/希尔排序/希尔排序/SortTestHelper.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SortTestHelper.swift 3 | // 测试辅助 4 | // 5 | // Created by yunna on 2019/6/5. 6 | // Copyright © 2019年 yunna. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | // 生成有n个元素的随机数组,每个元素的随机范围为[rangeL, rangeR] 12 | func generateRandomArray(count:Int,rangL:Int,rangR:Int) -> Array { 13 | 14 | if rangR <= rangL{ 15 | fatalError("取值范围不准确") 16 | } 17 | 18 | var arr:[Int] = Array() 19 | for _ in 0.. Bool { 32 | for i in 0.. arr[i+1] { 34 | return false 35 | } 36 | } 37 | return true 38 | } 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /数据结构/线段树/线段树/线段树/Leetcode303.swift: -------------------------------------------------------------------------------- 1 | 2 | // 3 | // Leetcode303.swift 4 | // 线段树 5 | // 6 | // Created by yunna on 2019/5/10. 7 | // Copyright © 2019年 yunna. All rights reserved. 8 | // 9 | 10 | import Cocoa 11 | 12 | class NumArray { 13 | 14 | private 15 | var sum:[Int] = Array() 16 | init(_ nums: [Int]) { 17 | var numArr = nums 18 | numArr.append(0) 19 | sum.append(0) 20 | for index in 1.. Int { 27 | return sum[j+1] - sum[i]; 28 | } 29 | } 30 | 31 | 32 | 33 | 34 | class NumArray1 { 35 | 36 | private 37 | var segment = SegmentTree() 38 | 39 | init(_ nums: [Int]) { 40 | segment.SegmentTree(data: nums) 41 | } 42 | 43 | func sumRange(_ i: Int, _ j: Int) -> Int { 44 | let result = segment.query(queryL: i, queryR: j) 45 | return result 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /数据结构/栈和队列/栈和队列/栈和队列/Queue.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Queue.swift 3 | // 栈和队列 4 | // 5 | // Created by yunna on 2019/3/21. 6 | // Copyright © 2019年 yunna. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | class Queue: NSObject { 12 | private var queueArr = [Any]() 13 | 14 | //队列大小 15 | func getSize() -> Int { 16 | return queueArr.count 17 | } 18 | //入队 19 | func enqueue(E:Any) { 20 | queueArr.append(E) 21 | } 22 | //出队 23 | func dequeue() -> Any { 24 | let res = queueArr[0] 25 | queueArr.remove(at: 0) 26 | return res 27 | } 28 | //打印 29 | func toString() -> String { 30 | var res = "Queue: " 31 | for (index,item) in queueArr.enumerated() { 32 | if (index < queueArr.count - 1){ 33 | res.append("\(item),") 34 | } 35 | if (index == queueArr.count - 1){ 36 | res.append("\(item)") 37 | } 38 | } 39 | res.append(" ->tail") 40 | return res 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /算法/算法/数据结构/链表/DoubleList.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DoubleList.swift 3 | // 算法 4 | // 5 | // Created by 青青 on 2021/8/8. 6 | // 7 | 8 | import Cocoa 9 | 10 | class DoubleList{ 11 | //头尾虚节点 12 | var head, tail : Node? 13 | //链表元素数 14 | var size : Int = 0 15 | 16 | 17 | //在链表头部添加节点x 18 | public func addFirst(_ x : Node){ 19 | x.left = head?.right 20 | x.left = head 21 | head?.right?.right = x 22 | head?.right = x 23 | size += 1 24 | } 25 | 26 | //删除链表中的节点(x一定存在) 27 | public func remove(_ x : Node){ 28 | x.left?.right = x.right 29 | x.right?.left = x.left 30 | size -= 1 31 | } 32 | 33 | //删除链表最后一个节点,并返回该节点 34 | public func removeLast()-> Node?{ 35 | if tail?.left?.e == head?.e { 36 | return nil 37 | } 38 | let last = tail?.left 39 | remove(last!) 40 | return last 41 | } 42 | 43 | //返回链表长度 44 | public func Size()->Int{ 45 | return size 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /算法/算法/面试题/21-判断二叉树是否对称/Solution21.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Solution21.swift 3 | // 算法 4 | // 5 | // Created by 青青 on 2021/8/8. 6 | /* 7 | 判断二叉树是否对称 8 | 9 | 乍一看无从下手,但用递归其实很好解决。 10 | 11 | 根据题目的描述,镜像对称,就是左右两边相等,也就是左子树和右子树是相当的。 12 | 注意这句话,左子树和右子相等,也就是说要递归的比较左子树和右子树。 13 | 我们将根节点的左子树记做 left,右子树记做 right。比较 left 是否等于 right,不等的话直接返回就可以了。 14 | 15 | */ 16 | 17 | import Cocoa 18 | 19 | class Solution21: NSObject { 20 | func isSymmetric(node: Node?) -> Bool { 21 | if node == nil { 22 | return false 23 | } 24 | return dfs(left: node?.left, right: node?.right) 25 | } 26 | func dfs(left:Node?, right: Node?) -> Bool { 27 | if left == nil && right == nil { 28 | return true 29 | } 30 | if left == nil || right == nil { 31 | return false 32 | } 33 | if left?.e != right?.e { 34 | return false 35 | } 36 | 37 | //再递归的比较 左节点的左孩子 和 右节点的右孩子 38 | //以及比较 左节点的右孩子 和 右节点的左孩子 39 | return dfs(left: left?.right, right: right?.left) && dfs(left: left?.left, right: right?.right) 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /数据结构/栈和队列/栈和队列/栈和队列/main.swift: -------------------------------------------------------------------------------- 1 | // 2 | // main.swift 3 | // 栈和队列 4 | // 5 | // Created by yunna on 2019/3/20. 6 | // Copyright © 2019年 yunna. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | extension Date { 11 | /// 获取当前 毫秒级 时间戳 - 13位 12 | var milliStamp : Int { 13 | let timeInterval: TimeInterval = self.timeIntervalSince1970 14 | let millisecond = CLongLong(round(timeInterval*1000)) 15 | return Int(millisecond) 16 | } 17 | } 18 | 19 | //循环队列 20 | let loopQuere = LoopQueue() 21 | let startDate1 = Date().milliStamp 22 | for index in 0..<100{ 23 | loopQuere.enqueue(E: "\(index)") 24 | if index % 2 == 1{ 25 | let _ = loopQuere.dequeue() 26 | } 27 | 28 | } 29 | let endDate1 = Date().milliStamp 30 | print("循环队列:\(endDate1 - startDate1)") 31 | 32 | 33 | //线性队列 34 | let quere = Queue() 35 | let startDate2 = Date().milliStamp 36 | for index in 0..<100000{ 37 | quere.enqueue(E: "\(index)") 38 | if index % 2 == 1{ 39 | let _ = quere.dequeue() 40 | } 41 | } 42 | let endDate2 = Date().milliStamp 43 | print("线性队列:\(endDate2 - startDate1)") 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /算法/算法/面试题/14-寻找单向链表的倒数第k个节点/Solution14.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Solution14.swift 3 | // 算法 4 | // 5 | // Created by 青青 on 2021/8/8. 6 | /* 7 | 寻找单向链表的倒数第k个节点 8 | 9 | 10 | 查找单链表中倒数第k个节点,拿到这道题,我们首先肯定能想到先遍历链表得到节点总数n,然后再去从头往后走n-k+1步就能得到想要的节点。这种方法当然是可行的,也是比较容易想到的方法,但是有的宝宝就会想这样做要遍历两次链表,有没有什么方法只遍历一次链表就能找到节点 11 | 12 | 办法当然还是有的,我们可以利用两个指针遍历链表。刚开始把两个指针pAhead和pBhead都放在链表的头指针处,让pAhead向后走k步,pBhead保持不动,此时两个指针之间的距离就是k-1,然后让两个指针保持间距同时向后走,当pAhead走过链表最后一个节点到达NULL时,pBhead所指向的节点就是我们要找的倒数第k个节点 13 | 14 | */ 15 | 16 | import Cocoa 17 | 18 | class Solution14: NSObject { 19 | 20 | func findKthToTail(node: Node?, k: Int) -> Node? { 21 | if node == nil { 22 | return nil 23 | } 24 | var slowNode: Node? = node 25 | var fastNode: Node? = node 26 | 27 | for _ in 0.. Array { 13 | var sortArr = arr 14 | for i in 0.. sortArr[j+1] { 17 | sortArr.swapAt(j, j+1) 18 | } 19 | } 20 | } 21 | return sortArr 22 | } 23 | 24 | //冒泡排序 25 | func sorting1(arr: [Int]) -> Array { 26 | var sortArr = arr 27 | var swapped = false 28 | for i in 0.. sortArr[j+1] { 32 | sortArr.swapAt(j, j+1) 33 | swapped = true 34 | } 35 | } 36 | // 证明已经排序结束 37 | if swapped == false { 38 | return sortArr 39 | } 40 | } 41 | return sortArr 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /算法/算法/面试题/16-最大子序和/Solution16.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Solution16.swift 3 | // 算法 4 | // 5 | // Created by 青青 on 2021/8/8. 6 | /* 7 | 给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。 8 | 9 | 示例: 10 | 输入: [-2,1,-3,4,-1,2,1,-5,4], 11 | 输出: 6 12 | 解释: 连续子数组 [4,-1,2,1] 的和最大,为 6。 13 | 14 | 15 | 思路 16 | 17 | 当我们加上一个正数时,和会增加;当我们加上一个负数时,和会减少。如果当前得到的和是个负数,那么这个和在接下来的累加中应该抛弃并重新清零,不然的话这个负数将会减少接下来的和 18 | 19 | 设sum[i]为以第i个元素结尾且和最大的连续子数组。假设对于元素i,所有以它前面的元素结尾的子数组的长度都已经求得,那么以第i个元素结尾且和最大的连续子数组实际上,要么是以第i-1个元素结尾且和最大的连续子数组加上这个元素,要么是只包含第i个元素,即sum[i] 20 | = max(sum[i-1] + a[i], a[i])。 21 | 22 | 可以通过判断sum[i-1] + a[i]是否大于a[i]来做选择,而这实际上等价于判断sum[i-1]是否大于0。由于每次运算只需要前一次的结果,因此并不需要像普通的动态规划那样保留之前所有的计算结果,只需要保留上一次的即可,因此算法的时间和空间复杂度都很小 23 | */ 24 | 25 | import Cocoa 26 | 27 | class Solution16: NSObject { 28 | func maxSubArray(nums: [Int]) -> Int { 29 | var max = nums[0] 30 | var stmp = 0 31 | for i in 0.. 0 { 33 | stmp += nums[i] 34 | } else { 35 | stmp = nums[i] 36 | } 37 | 38 | if stmp > max { 39 | max = stmp 40 | } 41 | } 42 | 43 | return max 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /排序/冒泡排序/Sort/Sort/BubbleSorting.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Sorting.swift 3 | // Sort 4 | // 5 | // Created by yunna on 2019/6/3. 6 | // Copyright © 2019年 yunna. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | class BubbleSorting: NSObject { 12 | 13 | //冒泡排序 14 | func sorting(arr:[Int]) -> Array{ 15 | var sortArr:[Int] = arr 16 | for i in 0.. sortArr[j+1]{ 19 | sortArr.swapAt(j, j+1) 20 | } 21 | } 22 | } 23 | return sortArr 24 | } 25 | 26 | 27 | //冒泡排序 28 | func sorting1(arr:[Int]) -> Array{ 29 | var sortArr:[Int] = arr 30 | print(sortArr) 31 | var swapped = false 32 | for i in 0.. sortArr[j+1]{ 36 | sortArr.swapAt(j, j+1) 37 | swapped = true 38 | } 39 | } 40 | if swapped == false{ 41 | break 42 | } 43 | } 44 | return sortArr 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /算法/算法/面试题/20-判断链表是否有环/Solution20.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Solution20.swift 3 | // 算法 4 | // 5 | // Created by 青青 on 2021/8/8. 6 | /* 7 | 有一个单向链表,链表当中有可能出现“环”,就像题图这样。如何用程序判断出这个链表是有环链表? 8 | 不允许修改链表结构。 9 | 时间复杂度O(n),空间复杂度O(1)。 10 | 11 | 12 | 首先创建两个指针1和2(在java里就是两个对象引用),同时指向这个链表的头节点。然后开始一个大循环,在循环体中,让指针1每次向下移动一个节点,让指针2每次向下移动两个节点,然后比较两个指针指向的节点是否相同。如果相同,则判断出链表有环,如果不同,则继续下一次循环。 13 | 14 | 例如链表A->B->C->D->B->C->D,两个指针最初都指向节点A,进入第一轮循环,指针1移动到了节点B,指针2移动到了C。第二轮循环,指针1移动到了节点C,指针2移动到了节点B。第三轮循环,指针1移动到了节点D,指针2移动到了节点D,此时两指针指向同一节点,判断出链表有环。 15 | 16 | 此方法也可以用一个更生动的例子来形容:在一个环形跑道上,两个运动员在同一地点起跑,一个运动员速度快,一个运动员速度慢。当两人跑了一段时间,速度快的运动员必然会从速度慢的运动员身后再次追上并超过,原因很简单,因为跑道是环形的 17 | 18 | 19 | */ 20 | 21 | import Cocoa 22 | 23 | class Solution20: NSObject { 24 | func isLoopList(node: Node?) -> Bool { 25 | if node == nil { 26 | return false 27 | } 28 | var fastNode = node 29 | var slowNode = node 30 | 31 | while fastNode != nil && slowNode != nil { 32 | fastNode = fastNode?.right?.right 33 | slowNode = slowNode?.right?.right 34 | if fastNode?.e == slowNode?.e { 35 | return true 36 | } 37 | } 38 | 39 | return false 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /数据结构/栈和队列/栈和队列/栈和队列/Solution.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Solution.swift 3 | // 栈和队列 4 | // 5 | // Created by yunna on 2019/3/20. 6 | // Copyright © 2019年 yunna. All rights reserved. 7 | // 8 | //{()}{}[] 9 | import Cocoa 10 | 11 | class Solution: NSObject { 12 | let stack = Stack() 13 | func isValid(_ s: String) -> Bool { 14 | for c in s { 15 | if (c == "(" || c == "[" || c == "{"){ 16 | stack.push(e: c) 17 | }else{ 18 | //第一个如果不是上面的那三种情况直接return false 19 | if stack.isEmpty() { 20 | return false 21 | } 22 | //在出现第一个右括号的时候,一定有一个与之相对应的左括号,否则不符合规则;在出现第一个右括号,我们在栈中栈定元素,栈定元素应该是与右括号成对出现的,否则不符合规则 23 | let topChar = stack.pop() as! Character 24 | if (c == ")" && topChar != "("){ 25 | return false 26 | } 27 | if (c == "]" && topChar != "["){ 28 | return false 29 | } 30 | if (c == "}" && topChar != "{"){ 31 | return false 32 | } 33 | } 34 | 35 | } 36 | //在字符串遍历到最后,我们的栈应该是空的,因为左侧右侧成对出现便会被压出栈 37 | return stack.isEmpty() 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /算法/算法/面试题/13-合并有序数组/Solution13.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Solution13.swift 3 | // 算法 4 | // 5 | // Created by 青青 on 2021/8/8. 6 | /* 7 | 给定两个有序整数数组 nums1 和 nums2,将 nums2 合并到 nums1 中,使得 num1 成为一个有序数组。 8 | 说明: 9 | 初始化 nums1 和 nums2 的元素数量分别为 m 和 n。 10 | 你可以假设 nums1 有足够的空间(空间大小大于或等于 m + n)来保存 nums2 中的元素。 11 | 12 | 先放到一个新的数组中,在排序。但是这样没有体现任何算法,这里考的不是快速排序等排序算法。关键是如何利用有序这个已知条件。可以这样想,假设两个源数组的长度不一样,那么假设其中短的数组用完了,即全部放入到新数组中去了,那么长数组中剩下的那一段就可以直接拿来放到新数组中去了。 13 | 14 | */ 15 | 16 | import Cocoa 17 | 18 | class Solution13: NSObject { 19 | 20 | func SortTwoArray(nums1: [Int], nums2: [Int]) -> [Int] { 21 | var data: [Int] = Array() 22 | var a = 0 23 | var b = 0 24 | while a < nums1.count && b < nums2.count { 25 | if nums1[a] > nums2[b] { 26 | data.append(nums1[a]) 27 | a += 1 28 | } else { 29 | data.append(nums2[b]) 30 | b += 1 31 | } 32 | } 33 | 34 | //nums1数组比较长 35 | while a < nums1.count { 36 | data.append(nums1[a]) 37 | a += 1 38 | } 39 | 40 | //nums2数组比较长 41 | while b < nums2.count { 42 | data.append(nums2[b]) 43 | b += 1 44 | } 45 | 46 | return data 47 | } 48 | 49 | 50 | } 51 | -------------------------------------------------------------------------------- /数据结构/栈和队列/栈和队列/栈和队列/Stack.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Stack.swift 3 | // 栈和队列 4 | // 5 | // Created by yunna on 2019/3/20. 6 | // Copyright © 2019年 yunna. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | class Stack: NSObject { 12 | private var stackArr = [Any]() 13 | 14 | //栈的大小 15 | func getSize() -> Int { 16 | return stackArr.count 17 | } 18 | //栈是否为空 19 | func isEmpty() -> Bool { 20 | return stackArr.isEmpty 21 | } 22 | //压栈 23 | func push(e:Any) { 24 | stackArr.append(e) 25 | } 26 | //出栈 27 | func pop() -> Any{ 28 | guard !stackArr.isEmpty else { 29 | return (Any).self 30 | } 31 | let E = stackArr[stackArr.count - 1] 32 | stackArr.removeLast() 33 | return E 34 | } 35 | //栈顶 36 | func peek() -> Any { 37 | return stackArr[stackArr.count - 1] 38 | } 39 | //打印 40 | func toString() ->String{ 41 | var res = "Stack: " 42 | for (index,item) in stackArr.enumerated() { 43 | if (index < stackArr.count - 1){ 44 | res.append("\(item),") 45 | } 46 | if (index == stackArr.count - 1){ 47 | res.append("\(item)") 48 | } 49 | } 50 | 51 | return res 52 | } 53 | 54 | 55 | } 56 | -------------------------------------------------------------------------------- /排序/基数排序/基数排序/基数排序/radixSort.swift: -------------------------------------------------------------------------------- 1 | // 2 | // radixSort.swift 3 | // 基数排序 4 | // 5 | // Created by yunna on 2019/6/29. 6 | // Copyright © 2019年 yunna. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | class radixSort: NSObject { 12 | private 13 | var data:[Int] = Array() 14 | 15 | func sort(arr:[Int]) -> [Int] { 16 | data = arr 17 | recursiveSort(num: 1) 18 | return data 19 | } 20 | //递归 21 | private 22 | func recursiveSort(num:Int) { 23 | let mode = num * 10 24 | var buckets = [[Int]]() 25 | for _ in 0..<10 { 26 | buckets.append([Int]()) 27 | } 28 | 29 | //判断递归是否结束,默认结束, 30 | //当取余的所有值都为0的时候证明已经遍历到了最高位,递归结束 31 | var end = true 32 | for item in data { 33 | let temp = (item % mode) / num 34 | if temp != 0{ 35 | end = false 36 | } 37 | var tempArr = buckets[temp] 38 | tempArr.append(item) 39 | buckets[temp] = tempArr 40 | } 41 | 42 | if end { 43 | return 44 | } 45 | 46 | //取出结果 47 | data.removeAll() 48 | for item in buckets { 49 | data += item 50 | } 51 | 52 | recursiveSort(num: mode) 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /数据结构/并查集/并查集/并查集/UnionFind1.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UnionFind1.swift 3 | // 并查集 4 | // 5 | // Created by yunna on 2019/5/15. 6 | // Copyright © 2019年 yunna. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | class UnionFind1: NSObject { 12 | 13 | // 我们的第一版Union-Find本质就是一个数组 14 | private var ids:[Int] = Array() 15 | 16 | init(size:Int) { 17 | super.init() 18 | // 初始化, 每一个id[i]指向自己, 没有合并的元素 19 | for item in 0.. Int { 27 | if p >= ids.count || p < 0 { 28 | fatalError("p is out of bound") 29 | } 30 | return ids[p] 31 | } 32 | 33 | // 查看元素p和元素q是否所属一个集合 34 | // O(1)复杂度 35 | func isConnected(p:Int,q:Int) -> Bool { 36 | return find(p) == find(q) 37 | } 38 | 39 | // 合并元素p和元素q所属的集合 40 | // O(n) 复杂度 41 | func unionElements(p:Int,q:Int) { 42 | let qId = find(q) 43 | let pId = find(p) 44 | 45 | if qId == pId { 46 | return 47 | } 48 | // 合并过程需要遍历一遍所有元素, 将两个元素的所属集合编号合并 49 | for (index,item) in ids.enumerated() { 50 | if (pId == item){ 51 | ids[index] = qId 52 | } 53 | } 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /排序/计数排序/计数排序/计数排序/CountingSort.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CountingSort.swift 3 | // 计数排序 4 | // 5 | // Created by yunna on 2019/6/28. 6 | // Copyright © 2019年 yunna. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | class CountingSort: NSObject { 12 | func sort(arr:[Int]) -> [Int] { 13 | var max:Int = arr[0] //最大值 14 | var min:Int = arr[0] //最小值 15 | 16 | for item in arr { 17 | //找出最大值 18 | max = max < item ? item : max 19 | //找出最小值 20 | min = min < item ? min : item 21 | } 22 | 23 | //创建一个元素个数为 max-min 的数组 24 | var list:[Int] = Array() 25 | for _ in 0..() 13 | var size = 0 14 | 15 | //在链表中添加元素 16 | func add(index: Int, e: String) { 17 | var current = dummyHead 18 | for _ in 0..() 20 | } 21 | size += 1 22 | current.right = Node(e: e, right: current.right) 23 | } 24 | 25 | //获取链表元素 26 | func get(index:Int) -> String? { 27 | var current = dummyHead 28 | for _ in 0..() 30 | } 31 | return current.e 32 | } 33 | 34 | // 修改链表元素 35 | func set(index:Int,e:String) { 36 | var current = dummyHead 37 | for _ in 0..() 39 | } 40 | current.e = e 41 | } 42 | 43 | //删除链表元素 44 | func delete(index:Int) { 45 | var current = dummyHead 46 | for _ in 0..() 48 | } 49 | current.right = current.right?.right 50 | size -= 1 51 | } 52 | 53 | 54 | } 55 | -------------------------------------------------------------------------------- /数据结构/并查集/并查集/并查集/UnionFind2.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UnionFind2.swift 3 | // 并查集 4 | // 5 | // Created by yunna on 2019/5/15. 6 | // Copyright © 2019年 yunna. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | class UnionFind2: NSObject { 12 | // 我们的第二版Union-Find, 使用一个数组构建一棵指向父节点的树 13 | // parent[i]表示第一个元素所指向的父节点 14 | private var parent:[Int] = Array() 15 | 16 | init(size:Int) { 17 | super.init() 18 | // 初始化, 每一个parent[i]指向自己, 表示每一个元素自己自成一个集合 19 | for item in 0.. Int { 27 | if p >= parent.count || p < 0 { 28 | fatalError("p is out of bound") 29 | } 30 | // 不断去查询自己的父亲节点, 直到到达根节点 31 | // 根节点的特点: parent[p] == p 32 | var root = p 33 | while root != parent[root] { 34 | root = parent[root] 35 | } 36 | 37 | return root 38 | } 39 | 40 | // 查看元素p和元素q是否所属一个集合 41 | // O(h)复杂度, h为树的高度 42 | func isConnected(p:Int,q:Int) -> Bool { 43 | return find(p) == find(q) 44 | } 45 | 46 | // 合并元素p和元素q所属的集合 47 | // O(h)复杂度, h为树的高度 48 | func unionElements(p:Int,q:Int) { 49 | let qRoot = find(q) 50 | let pRoot = find(p) 51 | if qRoot == pRoot { 52 | return 53 | } 54 | parent[qRoot] = pRoot 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /算法/算法/面试题/06-红、黄、蓝三种颜色的气球/Solution6.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Solution6.swift 3 | // 算法 4 | // 5 | // Created by 青青 on 2021/8/8. 6 | /* 7 | 有红、黄、蓝三种颜色的气球。在牛客王国,1个红气球+1个黄气球+1个蓝气球可以兑换一张彩票。 8 | 2个红气球+1个黄气球可以兑换1个蓝气球。 9 | 2个黄气球+1个蓝气球可以兑换1个红气球。 10 | 2个蓝气球+1个红气球可以兑换1个黄气球。 11 | 12 | 13 | 现在牛牛有a个红气球,b个黄气球, 14 | c个蓝气球,牛牛想知道自己最多可以兑换多少张彩票。 15 | 16 | 17 | 注意: 18 | 计算方式是,三个颜色,抵一张 19 | 先找出最小的,全部换掉, 20 | 然后打折兑换, 21 | 22 | 23 | 为什么 A / 3 , B / 2 24 | a 1 + b 1 + c 1 = 抵用 1 25 | c 1 = a 2 + b 1 26 | 总结下 27 | a 3 + b 2 = 抵用 1 28 | 29 | 还要注意 30 | 三种颜色,取 A 2 B 1 , 兑换 C 1, 31 | 排列组合有 6 种,只能取其中 3 钟 32 | 不可以随意换 33 | 34 | 例如, 35 | 开始第二种兑换, 36 | r 为 0, 指望不上 37 | 只能在剩下的两种颜色中,固定的比例换 38 | */ 39 | 40 | import Cocoa 41 | 42 | class Solution6: NSObject { 43 | func solve(a:Int,b:Int,c:Int) -> Int { 44 | let minimum = threeMin(a: a, b: b, c: c) 45 | var result = minimum 46 | let red = a - minimum 47 | let yellow = b - minimum 48 | let blue = c - minimum 49 | if red == 0 { 50 | result += min(yellow/3, blue/2) 51 | } else if yellow == 0 { 52 | result += min(blue/3, red/2) 53 | } else if blue == 0{ 54 | result += min(red/3, yellow/2) 55 | } 56 | 57 | return result 58 | } 59 | 60 | func threeMin(a:Int,b:Int,c:Int) -> Int { 61 | var min = a 62 | if min > b { 63 | min = b 64 | } 65 | if min > c { 66 | min = c 67 | } 68 | return min 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /排序/希尔排序/希尔排序/希尔排序/ShellSort.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ShellSort.swift 3 | // 希尔排序 4 | // 5 | // Created by yunna on 2019/6/26. 6 | // Copyright © 2019年 yunna. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | class ShellSort: NSObject { 12 | private 13 | var data:[Int] = Array() 14 | 15 | func shellSort(data:[Int]) -> [Int] { 16 | self.data = data 17 | let incremental = data.count / 2 18 | recursiveShellSort(incremental: incremental) 19 | 20 | self.data = sorting(arr: self.data) 21 | return self.data 22 | } 23 | 24 | /// 递归 25 | /// 26 | /// - Parameter incremental: 增量 27 | private 28 | func recursiveShellSort(incremental:Int) { 29 | if incremental == 0 { 30 | return 31 | } 32 | for index in 0.. b{ 36 | data.swapAt(index, index+incremental) 37 | } 38 | 39 | } 40 | 41 | recursiveShellSort(incremental: incremental / 2) 42 | } 43 | 44 | //插入排序 45 | func sorting(arr:[Int]) -> Array{ 46 | var sortArr = arr 47 | for i in 0..[Int]{ 17 | self.arr = arr 18 | recursiveQuickSort(l: 0, r: arr.count-1) 19 | return self.arr 20 | } 21 | 22 | 23 | //递归排序 24 | private 25 | func recursiveQuickSort(l:Int,r:Int) { 26 | if l >= r { 27 | return 28 | } 29 | 30 | let p = partition(l: l, r: r) 31 | recursiveQuickSort(l: l, r: p-1) 32 | recursiveQuickSort(l: p+1, r: r) 33 | 34 | } 35 | 36 | //对arr[l...r]部分进行partition 37 | //返回p,使得arr[l...p-1] < arr[p] arr[p+1...r] > arr[p] 38 | private 39 | func partition(l:Int,r:Int) -> Int { 40 | 41 | //======================================= 42 | let rand = Int(arc4random()) % (r - l + 1) + l 43 | arr.swapAt(rand, l) 44 | //====================================== 45 | 46 | let v = arr[l] 47 | 48 | //arr[l+1...j] < v arr[j+1...i] > v 49 | var j = l 50 | for i in l+1.. Array{ 15 | var sortArr:[Int] = arr 16 | print(sortArr) 17 | for i in 0.. sortArr[j+1]{ 20 | sortArr.swapAt(j, j+1) 21 | } 22 | 23 | // print(sortArr) 24 | 25 | } 26 | print("-------------------") 27 | print(sortArr) 28 | } 29 | 30 | return sortArr 31 | } 32 | 33 | 34 | //冒泡排序 35 | func sorting1(arr:[Int]) -> Array{ 36 | var sortArr:[Int] = arr 37 | print(sortArr) 38 | var swapped = false 39 | for i in 0.. sortArr[j+1]{ 43 | sortArr.swapAt(j, j+1) 44 | swapped = true 45 | } 46 | // print(sortArr) 47 | } 48 | if swapped == false{ 49 | break 50 | } 51 | 52 | print("-------------------") 53 | print(sortArr) 54 | } 55 | 56 | return sortArr 57 | } 58 | 59 | 60 | 61 | } 62 | -------------------------------------------------------------------------------- /排序/简单排序算法/Sort/Sort/BubbleSorting.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Sorting.swift 3 | // Sort 4 | // 5 | // Created by yunna on 2019/6/3. 6 | // Copyright © 2019年 yunna. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | class BubbleSorting: NSObject { 12 | 13 | //冒泡排序 14 | func sorting(arr:[Int]) -> Array{ 15 | var sortArr:[Int] = arr 16 | print(sortArr) 17 | for i in 0.. sortArr[j+1]{ 20 | sortArr.swapAt(j, j+1) 21 | } 22 | 23 | // print(sortArr) 24 | 25 | } 26 | print("-------------------") 27 | print(sortArr) 28 | } 29 | 30 | return sortArr 31 | } 32 | 33 | 34 | //冒泡排序 35 | func sorting1(arr:[Int]) -> Array{ 36 | var sortArr:[Int] = arr 37 | print(sortArr) 38 | var swapped = false 39 | for i in 0.. sortArr[j+1]{ 43 | sortArr.swapAt(j, j+1) 44 | swapped = true 45 | } 46 | // print(sortArr) 47 | } 48 | if swapped == false{ 49 | break 50 | } 51 | 52 | print("-------------------") 53 | print(sortArr) 54 | } 55 | 56 | return sortArr 57 | } 58 | 59 | 60 | 61 | } 62 | -------------------------------------------------------------------------------- /排序/选择排序/Sort/Sort/BubbleSorting.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Sorting.swift 3 | // Sort 4 | // 5 | // Created by yunna on 2019/6/3. 6 | // Copyright © 2019年 yunna. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | class BubbleSorting: NSObject { 12 | 13 | //冒泡排序 14 | func sorting(arr:[Int]) -> Array{ 15 | var sortArr:[Int] = arr 16 | print(sortArr) 17 | for i in 0.. sortArr[j+1]{ 20 | sortArr.swapAt(j, j+1) 21 | } 22 | 23 | // print(sortArr) 24 | 25 | } 26 | print("-------------------") 27 | print(sortArr) 28 | } 29 | 30 | return sortArr 31 | } 32 | 33 | 34 | //冒泡排序 35 | func sorting1(arr:[Int]) -> Array{ 36 | var sortArr:[Int] = arr 37 | print(sortArr) 38 | var swapped = false 39 | for i in 0.. sortArr[j+1]{ 43 | sortArr.swapAt(j, j+1) 44 | swapped = true 45 | } 46 | // print(sortArr) 47 | } 48 | if swapped == false{ 49 | break 50 | } 51 | 52 | print("-------------------") 53 | print(sortArr) 54 | } 55 | 56 | return sortArr 57 | } 58 | 59 | 60 | 61 | } 62 | -------------------------------------------------------------------------------- /算法/算法/面试题/02-链表是否为回文结构/Solution2.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Solution2.swift 3 | // 算法 4 | // 5 | // Created by 青青 on 2021/8/7. 6 | /* 7 | 【题目】 给定一个链表的头节点head,请判断该链表是否为回 文结构。 8 | 9 | 例如: 10 | 1->2->1,返回true1->2->2->1,返回true 11 | 15->6->15,返回true 12 | 1->2->3,返回false 13 | 14 | 【具体思路】 15 | (1) 第一步,先求出链表的总长度 16 | (2) 第二步,找到中间节点 17 | (3) 第三步,逆置链表后半部分节点 18 | (4) 第四步,判断链表是否是回文结构 19 | */ 20 | 21 | import Cocoa 22 | 23 | class Solution2: NSObject { 24 | func checkPalindrome(headNode: Node) -> Bool { 25 | var head: Node? = headNode 26 | 27 | //1.找到链表的中间节点 (快慢指针) 28 | var fastNode: Node? = head 29 | var slowNode: Node? = head 30 | while fastNode != nil && fastNode?.right != nil { 31 | fastNode = fastNode?.right?.right 32 | slowNode = slowNode?.right 33 | } 34 | 35 | //2.翻转中间链表之后的节点 36 | // 此时slownode就是中间节点 37 | var midNode = slowNode?.right 38 | while midNode != nil { 39 | let mideNext = midNode?.right 40 | midNode?.right = slowNode 41 | slowNode = midNode 42 | midNode = mideNext 43 | } 44 | //3.对比是否是首尾相同的回文 45 | while slowNode != head { 46 | //当他两没遇到时 47 | if slowNode?.e != head?.e { 48 | return false 49 | } 50 | //偶数情况下 51 | if head?.right == slowNode { 52 | return true 53 | } 54 | slowNode = slowNode?.right 55 | head = head?.right 56 | } 57 | return true 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /算法/算法/面试题/01-有效的括号/Solution1.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Solution.swift 3 | // 算法 4 | // 5 | // Created by 青青 on 2021/8/7. 6 | /* 7 | 8 | 给定一个只包括 (,),{,},[,] 的字符串,判断字符串是否有效。 9 | 10 | 有效字符串需满足: 11 | 12 | 1、左括号必须用相同类型的右括号闭合。 13 | 2、左括号必须以正确的顺序闭合。 14 | 15 | 注意空字符串可被认为是有效字符串。 16 | 17 | 示例 1: 18 | 输入: "()" 19 | 输出: true 20 | 21 | 示例 2: 22 | 输入: "()[]{}" 23 | 输出: true 24 | 25 | 示例 3: 26 | 输入: "(]" 27 | 输出: false 28 | 29 | 示例 4: 30 | 输入: "([)]" 31 | 输出: false 32 | */ 33 | 34 | import Cocoa 35 | 36 | class Solution1: NSObject { 37 | let stack = Stack() 38 | func isValid(_ s: String) -> Bool { 39 | for c in s { 40 | // 如果是左括号压栈 41 | if c == "(" || c == "[" || c == "{" { 42 | stack.push(e: c) 43 | } else { 44 | //第一个如果不是上面的那三种情况直接return false 45 | if stack.stackArr.isEmpty { 46 | return false 47 | } 48 | //在出现第一个右括号的时候,一定有一个与之相对应的左括号,否则不符合规则; 49 | //在出现第一个右括号,我们在栈中栈定元素,栈定元素应该是与右括号成对出现的,否则不符合规则 50 | let topChar = stack.pop() as! Character 51 | if c == ")" && topChar != "(" { 52 | return false 53 | } 54 | if c == "]" && topChar != "[" { 55 | return false 56 | } 57 | if c == "}" && topChar != "{" { 58 | return false 59 | } 60 | 61 | } 62 | 63 | } 64 | 65 | //在字符串遍历到最后,我们的栈应该是空的,因为左侧右侧成对出现便会被压出栈 66 | return stack.stackArr.isEmpty 67 | } 68 | 69 | } 70 | -------------------------------------------------------------------------------- /排序/冒泡排序/冒泡排序.md: -------------------------------------------------------------------------------- 1 | # 冒泡排序(Bubble Sort) 2 | 3 | 冒泡排序是最基本最简单的排序了,在大家刚开始学习 C 语言的时候就会接触到。基本的思想就是,对于一个数比较与之相邻的数字,例如要把一个数列按从小到大的顺序排列,就拿左边第一个数,和第二的比,若小于第二个数两个交换,否则不换,再比较第二个和第三个,按照同样的规则,继续第三第四…直到最后。这样就算一次冒泡,每次冒泡都会有一个数被放到了最终的位置。 4 | 5 | 总的来说冒泡排序就是把最大的数放到最后面的位置 6 | 7 | 8 | 9 | ![](https://github.com/SunshineBrother/LeetCodeStudy/blob/master/算法/冒泡排序/bubbleSort.gif) 10 | 11 | 12 | 13 | ``` 14 | //冒泡排序 15 | func BubbleSorting(arr:[Int]) -> Array{ 16 | var sortArr:[Int] = arr 17 | for i in 0.. sortArr[j+1]{ 20 | sortArr.swapAt(j, j+1) 21 | } 22 | } 23 | } 24 | return sortArr 25 | } 26 | ``` 27 | 28 | ![](https://github.com/SunshineBrother/LeetCodeStudy/blob/master/算法/简单排序算法/冒泡排序.png) 29 | 30 | 31 | 我们查看打印发现,其实最后1次遍历其实是不用了,因为已经排序号结果了,但是因为我们没有进行判断,所以多运行了1次。我们的数据量比较小,所以多一次少一次没太大的性能差异,但是当数据量比较大的时候性能差异就显示出来了。为此我们继续进行优化 32 | 33 | 34 | 35 | ``` 36 | //冒泡排序 37 | func BubbleSorting1(arr:[Int]) -> Array{ 38 | var sortArr:[Int] = arr 39 | var swapped = false 40 | for i in 0.. sortArr[j+1]{ 44 | sortArr.swapAt(j, j+1) 45 | swapped = true 46 | } 47 | } 48 | if swapped == false{ 49 | break 50 | } 51 | } 52 | 53 | return sortArr 54 | } 55 | ``` 56 | 在判断语句中,如果一次都没有进行位置交换的话,证明已经排序完成了,这个时候可以停止遍历,结束循环 57 | 58 | ![](https://github.com/SunshineBrother/LeetCodeStudy/blob/master/算法/简单排序算法/冒泡排序1.png) 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /排序/计数排序/计数排序.md: -------------------------------------------------------------------------------- 1 | # 计数排序 2 | 3 | 计数排序的核心在于将输入的数据值转化为键存储在额外开辟的数组空间中。作为一种线性时间复杂度的排序,计数排序要求输入的数据必须是有确定范围的整数。 4 | 5 | 它是一个不基于比较的排序算法。不管是快排,归并,还是堆排,它们都难以突破NlogN的运行时间下限,而计数排序是一个线性时间级别的排序算法。对NlogN的突破凭借的就是不基于比较对元素进行排序,当然了,它也有很大的局限性,比如它只能对整数进行排序。总之,计数排序是一种对整数进行排序非常有效的排序算法。 6 | 7 | -(1)找出待排序的数组中最大和最小的元素 8 | 9 | -(2)统计数组中每个值为i的元素出现的次数,存入数组C的第i项 10 | 11 | -(3)对所有的计数累加(从C中的第一个元素开始,每一项和前一项相加) 12 | 13 | -(4)反向填充目标数组:将每个元素i放在新数组的第C(i)项,每放一个元素就将C(i)减去1 14 | 15 | 16 | ![](https://github.com/SunshineBrother/LeetCodeStudy/blob/master/算法/计数排序/计数排序.gif) 17 | 18 | ## 代码实现 19 | 20 | ``` 21 | func sort(arr:[Int]) -> [Int] { 22 | var max:Int = arr[0] //最大值 23 | var min:Int = arr[0] //最小值 24 | 25 | for item in arr { 26 | //找出最大值 27 | max = max < item ? item : max 28 | //找出最小值 29 | min = min < item ? min : item 30 | } 31 | 32 | //创建一个元素个数为 max-min 的数组 33 | var list:[Int] = Array() 34 | for _ in 0.. Array { 13 | sortArr = arr 14 | recursive(l: 0, r: arr.count-1) 15 | return sortArr 16 | } 17 | 18 | //递归排序 19 | private func recursive(l:Int,r:Int) { 20 | if l > r {return} 21 | let middle = partition(l: l, r: r) 22 | recursive(l: l, r: middle) 23 | recursive(l: middle+1, r: r) 24 | } 25 | 26 | //对arr[l...r]部分进行partition 27 | //返回p,使得arr[l...p-1] < arr[p] arr[p+1...r] > arr[p] 28 | private func partition(l:Int,r:Int) -> Int { 29 | let pivot = sortArr[l] 30 | var left = l 31 | var right = r 32 | while left < right { 33 | // 右向左扫描 34 | while left < right { 35 | if sortArr[right] >= pivot { 36 | right -= 1 37 | } else { 38 | sortArr[left] = sortArr[right] 39 | left += 1 40 | break 41 | } 42 | 43 | } 44 | // 左向右扫描 45 | while left < right { 46 | if sortArr[left] > pivot { 47 | left += 1 48 | } else { 49 | sortArr[right] = sortArr[left] 50 | right -= 1 51 | break 52 | } 53 | } 54 | 55 | } 56 | 57 | sortArr[left] = pivot 58 | return left 59 | } 60 | 61 | } 62 | -------------------------------------------------------------------------------- /数据结构/Trie/Trie/Trie/leetCode211.swift: -------------------------------------------------------------------------------- 1 | // 2 | // leetCode211.swift 3 | // Trie 4 | // 5 | // Created by yunna on 2019/5/14. 6 | // Copyright © 2019年 yunna. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | class leetCode211: NSObject { 12 | private var root = Node() //节点 13 | 14 | func addWord(_ word: String) { 15 | var cur = root 16 | for item in word { 17 | //如果下个节点不包含,添加 18 | if !cur.next.keys.contains(item){ 19 | let node = Node() 20 | cur.next[item] = node 21 | } 22 | //找到下一个节点 23 | cur = cur.next[item] ?? Node() 24 | } 25 | 26 | //如果已经存在这个单词了,不处理 27 | //如果不存在这个单词,size++,isWord设置为true 28 | if !cur.isWord { 29 | cur.isWord = true 30 | } 31 | } 32 | 33 | func search(_ word: String) -> Bool { 34 | let result = recursiveSearch(word: word, cur: root, index: 0) 35 | return result 36 | } 37 | 38 | func recursiveSearch(word: String,cur:Node,index:Int) -> Bool { 39 | if index == word.count { 40 | return cur.isWord 41 | } 42 | 43 | let c:Character = word.first! 44 | let a:Character = "." 45 | if c != a { 46 | if !cur.next.keys.contains(c){ 47 | return false 48 | } 49 | 50 | return recursiveSearch(word: word, cur: cur.next[c]!, index: index+1) 51 | }else{ 52 | for key in cur.next.keys{ 53 | if(recursiveSearch(word: word, cur: cur.next[key]!, index: index+1)){ 54 | return true 55 | } 56 | } 57 | return false 58 | } 59 | 60 | } 61 | 62 | } 63 | 64 | -------------------------------------------------------------------------------- /排序/归并排序/归并排序/归并排序/MergeSort.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MergeSort.swift 3 | // 归并排序 4 | // 5 | // Created by yunna on 2019/6/4. 6 | // Copyright © 2019年 yunna. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | class MergeSort: NSObject { 12 | private 13 | var sortArr:[Int] = Array() 14 | func sort(arr:[Int]) ->Array{ 15 | sortArr = arr 16 | recursiveSort(l: 0, r: arr.count-1) 17 | return sortArr 18 | } 19 | 20 | func recursiveSort(l:Int,r:Int) { 21 | if l >= r { 22 | return 23 | } 24 | 25 | let mid = (l+r)/2 26 | //左边归并排序,使得左子序列有序 27 | recursiveSort(l: l, r: mid) 28 | //右边归并排序,使得右子序列有序 29 | recursiveSort(l: mid+1, r: r) 30 | //将两个有序子数组合并操作 31 | merge(l: l, mid: mid, r: r) 32 | } 33 | 34 | // 将arr[l...mid]和arr[mid+1...r]两部分进行归并 35 | func merge(l:Int,mid:Int,r:Int) { 36 | 37 | var aux:[Int] = Array() 38 | for i in l.. mid){// 如果左半部分元素已经全部处理完毕 48 | sortArr[k] = aux[j-l] 49 | j += 1 50 | } 51 | else if( j > r ){ // 如果右半部分元素已经全部处理完毕 52 | sortArr[k] = aux[i-l]; 53 | i += 1 54 | } 55 | else if(aux[i-l] - aux[j-l] < 0 ){ // 左半部分所指元素 < 右半部分所指元素 56 | sortArr[k] = aux[i-l] 57 | i += 1 58 | } 59 | else{ // 左半部分所指元素 >= 右半部分所指元素 60 | sortArr[k] = aux[j-l] 61 | j += 1 62 | } 63 | } 64 | } 65 | 66 | } 67 | -------------------------------------------------------------------------------- /数据结构/并查集/并查集/并查集/UnionFind3.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UnionFind3.swift 3 | // 并查集 4 | // 5 | // Created by yunna on 2019/5/15. 6 | // Copyright © 2019年 yunna. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | class UnionFind3: NSObject { 12 | // parent[i]表示第一个元素所指向的父节点 13 | private var parent:[Int] = Array() 14 | // 表示以i为根的集合中元素个数 15 | private var sz:[Int] = Array() 16 | init(size:Int) { 17 | super.init() 18 | // 初始化, 每一个parent[i]指向自己, 表示每一个元素自己自成一个集合 19 | for item in 0.. Int { 29 | if p >= parent.count || p < 0 { 30 | fatalError("p is out of bound") 31 | } 32 | // 不断去查询自己的父亲节点, 直到到达根节点 33 | // 根节点的特点: parent[p] == p 34 | var root = p 35 | while root != parent[root] { 36 | root = parent[root] 37 | } 38 | 39 | return root 40 | } 41 | 42 | // 查看元素p和元素q是否所属一个集合 43 | // O(h)复杂度, h为树的高度 44 | func isConnected(p:Int,q:Int) -> Bool { 45 | return find(p) == find(q) 46 | } 47 | 48 | // 合并元素p和元素q所属的集合 49 | // O(h)复杂度, h为树的高度 50 | func unionElements(p:Int,q:Int) { 51 | let qRoot = find(q) 52 | let pRoot = find(p) 53 | if qRoot == pRoot { 54 | return 55 | } 56 | 57 | // 根据两个元素所在树的元素个数不同判断合并方向 58 | // 将元素个数少的集合合并到元素个数多的集合上 59 | if sz[pRoot] < sz[qRoot] { 60 | parent[pRoot] = qRoot 61 | sz[qRoot] += sz[pRoot] 62 | }else{ 63 | parent[qRoot] = pRoot; 64 | sz[pRoot] += sz[qRoot]; 65 | } 66 | 67 | } 68 | 69 | 70 | } 71 | -------------------------------------------------------------------------------- /数据结构/并查集/并查集/并查集/UnionFind4.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UnionFind4.swift 3 | // 并查集 4 | // 5 | // Created by yunna on 2019/5/15. 6 | // Copyright © 2019年 yunna. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | class UnionFind4: NSObject { 12 | // parent[i]表示第一个元素所指向的父节点 13 | private var parent:[Int] = Array() 14 | // rank[i]表示以i为根的集合所表示的树的层数 15 | private var rank:[Int] = Array() 16 | 17 | init(size:Int) { 18 | super.init() 19 | // 初始化, 每一个parent[i]指向自己, 表示每一个元素自己自成一个集合 20 | for item in 0.. Int { 29 | if p >= parent.count || p < 0 { 30 | fatalError("p is out of bound") 31 | } 32 | // 不断去查询自己的父亲节点, 直到到达根节点 33 | // 根节点的特点: parent[p] == p 34 | var root = p 35 | while root != parent[root] { 36 | root = parent[root] 37 | } 38 | 39 | return root 40 | } 41 | 42 | // 查看元素p和元素q是否所属一个集合 43 | // O(h)复杂度, h为树的高度 44 | func isConnected(p:Int,q:Int) -> Bool { 45 | return find(p) == find(q) 46 | } 47 | 48 | // 合并元素p和元素q所属的集合 49 | // O(h)复杂度, h为树的高度 50 | func unionElements(p:Int,q:Int) { 51 | let qRoot = find(q) 52 | let pRoot = find(p) 53 | if qRoot == pRoot { 54 | return 55 | } 56 | // 根据两个元素所在树的rank不同判断合并方向 57 | // 将rank低的集合合并到rank高的集合上 58 | if(rank[pRoot] < rank[qRoot]){ 59 | parent[pRoot] = qRoot; 60 | }else if(rank[qRoot] < rank[pRoot]){ 61 | parent[qRoot] = pRoot; 62 | }else{ // rank[pRoot] == rank[qRoot] 63 | parent[pRoot] = qRoot; 64 | rank[qRoot] += 1; // 此时, 我维护rank的值 65 | } 66 | 67 | } 68 | 69 | } 70 | -------------------------------------------------------------------------------- /算法/算法/面试题/07-两个单向链表的第一个公共节点/Solution7.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Solution7.swift 3 | // 算法 4 | // 5 | // Created by 青青 on 2021/8/8. 6 | /* 7 | 寻找两个单向链表的第一个公共节点 8 | 9 | 求差法 10 | 先遍历两个链表求出他们的长度,当链表重合时,重合节点的后面的节点长度相同,只有公共节点的前面的节点长度不同,求差,让长的链表先走差值的长度,然后求公共节点就简单许多 11 | */ 12 | 13 | import Cocoa 14 | 15 | class Solution7: NSObject { 16 | 17 | func getIntersectionNode(headA: Node?, headB: Node?) -> Node? { 18 | // 如果某一链表为空,肯定不存在公共节点 19 | if headA == nil || headB == nil { 20 | return nil 21 | } 22 | // 记录长度差 23 | let lengthA = getListLength(node: headA) 24 | let lengthB = getListLength(node: headB) 25 | var diff = 0 26 | var longNode: Node? = headA 27 | var shortNode: Node? = headB 28 | if lengthA > lengthB { 29 | diff = lengthA - lengthB 30 | } else { 31 | diff = lengthB - lengthA 32 | longNode = headB 33 | shortNode = headA 34 | } 35 | 36 | // 先在长链表中走若干步,直到剩余部分和短链表长度一致 37 | for _ in 0..?) -> Int { 58 | var size = 0 59 | var head = node 60 | 61 | while head != nil { 62 | size += 1 63 | head = head?.right 64 | } 65 | return size 66 | } 67 | 68 | } 69 | -------------------------------------------------------------------------------- /排序/基数排序/基数排序.md: -------------------------------------------------------------------------------- 1 | # 基数排序 2 | 3 | 4 | 基数排序是一种非比较型整数排序算法,其原理是将整数按位数切割成不同的数字,然后按每个位数分别比较。由于整数也可以表达字符串(比如名字或日期)和特定格式的浮点数,所以基数排序也不是只能使用于整数。 5 | 6 | 7 | - 将所有待比较数值(正整数)统一为同样的数位长度,数位较短的数前面补零 8 | - 从最低位开始,依次进行一次排序 9 | - 从最低位排序一直到最高位排序完成以后, 数列就变成一个有序序列 10 | 11 | 12 | ![](https://github.com/SunshineBrother/LeetCodeStudy/blob/master/算法/基数排序/radixSort.gif) 13 | 14 | 15 | 16 | ![](https://github.com/SunshineBrother/LeetCodeStudy/blob/master/算法/基数排序/基数排序.png) 17 | 18 | 19 | 20 | ## 代码实现 21 | 22 | ``` 23 | class radixSort: NSObject { 24 | 25 | private 26 | var data:[Int] = Array() 27 | 28 | func sort(arr:[Int]) -> [Int] { 29 | data = arr 30 | recursiveSort(num: 1) 31 | return data 32 | } 33 | //递归 34 | private 35 | func recursiveSort(num:Int) { 36 | let mode = num * 10 37 | var buckets = [[Int]]() 38 | for _ in 0..<10 { 39 | buckets.append([Int]()) 40 | } 41 | 42 | //判断递归是否结束,默认结束, 43 | //当取余的所有值都为0的时候证明已经遍历到了最高位,递归结束 44 | var end = true 45 | for item in data { 46 | let temp = (item % mode) / num 47 | if temp != 0{ 48 | end = false 49 | } 50 | var tempArr = buckets[temp] 51 | tempArr.append(item) 52 | buckets[temp] = tempArr 53 | } 54 | 55 | if end { 56 | return 57 | } 58 | 59 | //取出结果 60 | data.removeAll() 61 | for item in buckets { 62 | data += item 63 | } 64 | 65 | recursiveSort(num: mode) 66 | } 67 | 68 | } 69 | 70 | ``` 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | -------------------------------------------------------------------------------- /数据结构/链表/链表/链表/LeetCode.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LeetCode.swift 3 | // 链表 4 | // 5 | // Created by yunna on 2019/3/27. 6 | // Copyright © 2019年 yunna. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | class LeetCode: NSObject { 12 | 13 | func removeElements(_ head: ListNode?, _ val: Int) -> ListNode? { 14 | var headNode = head 15 | //判断第一个节点的val值是否是需要移除的那一个 16 | while headNode != nil && headNode?.val == val { 17 | headNode = headNode?.next 18 | } 19 | //第一个节点判断结束以后如果,链表为空,直接return 20 | if headNode == nil { 21 | return headNode 22 | } 23 | //判断剩余链表 24 | var prev:ListNode = headNode! 25 | while prev.next != nil { 26 | if prev.next?.val == val{ 27 | //下一个节点如果是要去除的值,去掉下一个节点值 28 | //把当前节点的下一个节点的值 修改为 当前节点下下一个节点的值,这个步骤是去掉下一个节点的值 29 | prev.next = prev.next?.next 30 | }else{ 31 | //遍历下一个节点 32 | prev = prev.next! 33 | } 34 | } 35 | 36 | return headNode 37 | } 38 | 39 | //如果要是使用虚拟头结点的话 40 | func removeElements1(_ head: ListNode?, _ val: Int) -> ListNode? { 41 | let dummyHead = ListNode(-1) 42 | dummyHead.next = head 43 | var prev:ListNode = dummyHead 44 | while prev.next != nil { 45 | if prev.next?.val == val{ 46 | //下一个节点如果是要去除的值,去掉下一个节点值 47 | //把当前节点的下一个节点的值 修改为 当前节点下下一个节点的值,这个步骤是去掉下一个节点的值 48 | prev.next = prev.next?.next 49 | }else{ 50 | //遍历下一个节点 51 | prev = prev.next! 52 | } 53 | } 54 | 55 | 56 | return dummyHead.next 57 | } 58 | 59 | 60 | 61 | } 62 | 63 | public class ListNode { 64 | public var val: Int 65 | public var next: ListNode? 66 | public init(_ val: Int) { 67 | self.val = val 68 | self.next = nil 69 | } 70 | } 71 | 72 | -------------------------------------------------------------------------------- /算法/算法/数据结构/二叉树/BTS.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BTS.swift 3 | // 算法 4 | // 5 | // Created by 青青 on 2021/8/7. 6 | // 7 | 8 | import Cocoa 9 | 10 | class BTS: NSObject { 11 | //根节点 12 | var root: Node? 13 | 14 | // 添加节点 15 | func add(e: Int) { 16 | if root == nil { 17 | root = Node(e: e, right: nil) 18 | } else { 19 | addNode(e: e, node: root) 20 | } 21 | } 22 | 23 | // 前序遍历以node为根的二分搜索树, 递归算法 24 | func preOrder(node: Node?) { 25 | if node == nil { 26 | return 27 | } 28 | print(node?.e as Any) 29 | preOrder(node: node?.left) 30 | preOrder(node: node?.right) 31 | } 32 | // 33 | func inOrder(node: Node?) { 34 | if node == nil { 35 | return 36 | } 37 | 38 | inOrder(node: node?.left) 39 | print(node?.e as Any) 40 | inOrder(node: node?.right) 41 | } 42 | 43 | // 44 | func postOrder(node: Node?) { 45 | if node == nil { 46 | return 47 | } 48 | 49 | postOrder(node: node?.left) 50 | postOrder(node: node?.right) 51 | print(node?.e as Any) 52 | } 53 | 54 | } 55 | 56 | extension BTS { 57 | // 向以node为根的二分搜索树中插入元素e,递归算法 58 | func addNode(e: Int, node: Node?) { 59 | guard let currentNode = node else { 60 | return 61 | } 62 | if currentNode.e == e { 63 | return 64 | } else if e < currentNode.e && currentNode.left == nil { 65 | // 添加到左子树 66 | currentNode.left = Node(e: e) 67 | return 68 | } else if e > currentNode.e && currentNode.right == nil { 69 | // 添加到右子树 70 | currentNode.right = Node(e: e) 71 | return 72 | } 73 | 74 | //递归调用 75 | if e < currentNode.e { 76 | addNode(e: e, node: currentNode.left) 77 | } else { 78 | addNode(e: e, node: currentNode.right) 79 | } 80 | } 81 | 82 | 83 | } 84 | -------------------------------------------------------------------------------- /排序/桶排序/桶排序/桶排序/BucketSort.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BucketSort.swift 3 | // 桶排序 4 | // 5 | // Created by yunna on 2019/6/28. 6 | // Copyright © 2019年 yunna. All rights reserved. 7 | //https://mp.weixin.qq.com/s/vn3KiV-ez79FmbZ36SX9lg 8 | 9 | import Cocoa 10 | 11 | class BucketSort: NSObject { 12 | 13 | ///桶容量大小 14 | private 15 | var bucket = 5 16 | 17 | func sort(arr:[Int]) -> [Int] { 18 | var max:Int = arr[0] //最大值 19 | var min:Int = arr[0] //最小值 20 | 21 | for item in arr { 22 | //找出最大值 23 | max = max < item ? item : max 24 | //找出最小值 25 | min = min < item ? min : item 26 | } 27 | //获取桶的个数 28 | let buckets = bucketCount(min: min, max: max, arr: arr) 29 | var bucketList:[[Int]] = Array() 30 | for _ in 0.. Int { 56 | let num1 = (max - min + 1) / bucket 57 | let num2 = (max - min + 1) % 5 > 0 ? 1 : 0 58 | 59 | return num1 + num2 60 | } 61 | //插入排序 62 | func insertSorting(arr:[Int]) -> [Int]{ 63 | var sortArr = arr 64 | for i in 0.. Int { 19 | return size 20 | } 21 | 22 | //向trie中添加一个单词 23 | func add(word:String) { 24 | var cur = root 25 | for item in word { 26 | //如果下个节点不包含,添加 27 | if !cur.next.keys.contains(item){ 28 | let node = Node() 29 | cur.next[item] = node 30 | } 31 | //找到下一个节点 32 | cur = cur.next[item] ?? Node() 33 | } 34 | 35 | //如果已经存在这个单词了,不处理 36 | //如果不存在这个单词,size++,isWord设置为true 37 | if !cur.isWord { 38 | cur.isWord = true 39 | size += 1 40 | } 41 | } 42 | 43 | 44 | //查询单词word是否在Trie中 45 | func contains(word:String) -> Bool { 46 | var cur = root 47 | for item in word { 48 | //如果下个节点不包含 49 | if !cur.next.keys.contains(item){ 50 | return false 51 | } 52 | //找到下一个节点 53 | cur = cur.next[item] ?? Node() 54 | } 55 | 56 | //即使已经word全部字符都在字典树中,我们也要看看最后一个isWord是否是一个true,对比`pan`和`panda` 57 | return cur.isWord 58 | } 59 | 60 | // 查询是否在Trie中有单词以prefix为前缀 61 | func isPrefix(word:String) -> Bool { 62 | var cur = root 63 | for item in word { 64 | //如果下个节点不包含 65 | if !cur.next.keys.contains(item){ 66 | return false 67 | } 68 | //找到下一个节点 69 | cur = cur.next[item] ?? Node() 70 | } 71 | return true 72 | } 73 | 74 | 75 | 76 | 77 | } 78 | 79 | class Node: NSObject { 80 | var isWord:Bool = false 81 | var next:[Character:Node] = Dictionary() 82 | } 83 | 84 | 85 | 86 | -------------------------------------------------------------------------------- /数据结构/链表/链表/链表/LinkedList.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LinkedList.swift 3 | // 链表 4 | // 5 | // Created by yunna on 2019/3/23. 6 | // Copyright © 2019年 yunna. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | class LinkedList: NSObject { 12 | private var dummyHead:Node = Node() //虚拟头节点 13 | private var size:Int! = 0 //链表中的元素个数 14 | override init() { 15 | super.init() 16 | dummyHead.E = "" 17 | dummyHead.next = Node() 18 | } 19 | // 获取链表中的元素个数 20 | func getSize() -> Int { 21 | return size 22 | } 23 | //在链表中添加元素 24 | func add(index:Int,E:String) { 25 | var prev = dummyHead 26 | for _ in 0.. String { 42 | var cur = dummyHead 43 | for _ in 0..\(cur.E)") 74 | } 75 | print(res + "->NULL") 76 | } 77 | } 78 | 79 | class Node: NSObject { 80 | var E:String = "" //元素 81 | var next:Node! //指向下一个节点 82 | override init() { 83 | super.init() 84 | } 85 | 86 | init(E:String,next:Node) { 87 | self.E = E 88 | self.next = next 89 | } 90 | 91 | } 92 | -------------------------------------------------------------------------------- /排序/桶排序/桶排序.md: -------------------------------------------------------------------------------- 1 | # 桶排序(BucketSort) 2 | 3 | 桶排序是计数排序的升级版。它利用了函数的映射关系,高效与否的关键就在于这个映射函数的确定。为了使桶排序更加高效,我们需要做到这两点: 4 | 5 | - 在额外空间充足的情况下,尽量增大桶的数量 6 | - 使用的映射函数能够将输入的 N 个数据均匀的分配到 K 个桶中 7 | 8 | **算法步骤** 9 | 10 | - 设置固定数量的空桶 11 | - 把数据放到对应的桶中 12 | - 对每个不为空的桶中数据进行排序。 13 | - 拼接不为空的桶中数据,得到结果 14 | 15 | 16 | 17 | ![](https://github.com/SunshineBrother/LeetCodeStudy/blob/master/算法/桶排序/桶排序.gif) 18 | 19 | 20 | ## 代码 21 | 22 | ``` 23 | class BucketSort: NSObject { 24 | 25 | ///桶容量大小 26 | private 27 | var bucket = 5 28 | 29 | func sort(arr:[Int]) -> [Int] { 30 | var max:Int = arr[0] //最大值 31 | var min:Int = arr[0] //最小值 32 | 33 | for item in arr { 34 | //找出最大值 35 | max = max < item ? item : max 36 | //找出最小值 37 | min = min < item ? min : item 38 | } 39 | //获取桶的个数 40 | let buckets = bucketCount(min: min, max: max, arr: arr) 41 | var bucketList:[[Int]] = Array() 42 | for _ in 0.. Int { 68 | let num1 = (max - min + 1) / bucket 69 | let num2 = (max - min + 1) % 5 > 0 ? 1 : 0 70 | 71 | return num1 + num2 72 | } 73 | //插入排序 74 | func insertSorting(arr:[Int]) -> [Int]{ 75 | var sortArr = arr 76 | for i in 0..Array{ 29 | sortArr = arr 30 | recursiveSort(l: 0, r: arr.count-1) 31 | return sortArr 32 | } 33 | 34 | 35 | func recursiveSort(l:Int,r:Int) { 36 | if l >= r { 37 | return 38 | } 39 | 40 | let mid = (l+r)/2 41 | //左边归并排序,使得左子序列有序 42 | recursiveSort(l: l, r: mid) 43 | //右边归并排序,使得右子序列有序 44 | recursiveSort(l: mid+1, r: r) 45 | //将两个有序子数组合并操作 46 | merge(l: l, mid: mid, r: r) 47 | } 48 | // 将arr[l...mid]和arr[mid+1...r]两部分进行归并 49 | func merge(l:Int,mid:Int,r:Int) { 50 | 51 | var aux:[Int] = Array() 52 | for i in l.. mid){// 如果左半部分元素已经全部处理完毕 62 | sortArr[k] = aux[j-l] 63 | j += 1 64 | } 65 | else if( j > r ){ // 如果右半部分元素已经全部处理完毕 66 | sortArr[k] = aux[i-l]; 67 | i += 1 68 | } 69 | else if(aux[i-l] - aux[j-l] < 0 ){ // 左半部分所指元素 < 右半部分所指元素 70 | sortArr[k] = aux[i-l] 71 | i += 1 72 | } 73 | else{ // 左半部分所指元素 >= 右半部分所指元素 74 | sortArr[k] = aux[j-l] 75 | j += 1 76 | } 77 | 78 | } 79 | 80 | 81 | } 82 | ``` 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | -------------------------------------------------------------------------------- /数据结构/优先队列/优先队列/优先队列/MaxHeap.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MaxHeap.swift 3 | // 优先队列 4 | // 5 | // Created by yunna on 2019/5/6. 6 | // Copyright © 2019年 yunna. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | class MaxHeap: NSObject { 12 | var data:[Int] = Array() //存放堆的数组 13 | 14 | // 返回堆中的元素个数 15 | func size() -> Int { 16 | return data.count 17 | } 18 | 19 | // 返回一个布尔值, 表示堆中是否为空 20 | func isEmpty() -> Bool { 21 | return data.isEmpty 22 | } 23 | 24 | // 返回完全二叉树的数组表示中,一个索引所表示的元素的父亲节点的索引 25 | func parent(index:Int) -> Int { 26 | if index == 0 { 27 | fatalError("index-0 doesn't have parent.") 28 | } 29 | return (index - 1) / 2; 30 | } 31 | 32 | // 返回完全二叉树的数组表示中,一个索引所表示的元素的左孩子节点的索引 33 | func leftChild(index:Int) -> Int { 34 | return index * 2 + 1 35 | } 36 | //返回完全二叉树的数组表示中,一个索引所表示的元素的右孩子节点的索引 37 | func rightChild(index:Int) -> Int { 38 | return index * 2 + 2 39 | } 40 | 41 | //添加元素 42 | func add(child:Int) { 43 | data.append(child) 44 | siftUp(i: data.count - 1) 45 | } 46 | //上浮过程 47 | private 48 | func siftUp(i:Int) { 49 | var child = i //当前节点索引 50 | while (i > 0) && (data[child] > data[self.parent(index: child)]) { 51 | //交换child 和 parent 52 | data.swapAt(self.parent(index: child), child) 53 | //索引上浮 54 | child = self.parent(index: child) 55 | } 56 | 57 | } 58 | 59 | 60 | //删除元素 61 | func remove() -> Int { 62 | let last = data[data.count - 1] 63 | //索引为0的位置和最大的位置交换 64 | data.swapAt(0, data.count-1) 65 | //移除最后一位 66 | data.removeLast() 67 | //下沉 68 | siftDown(i: 0) 69 | 70 | return last 71 | } 72 | //下沉过程 73 | private 74 | func siftDown(i:Int) { 75 | var k = i 76 | while leftChild(index: k) < data.count { 77 | //j 是左右孩子中最大值 78 | var j = leftChild(index: k) 79 | if (j+1 < data.count) && (data[j+1] > data[j]){ 80 | j += 1 81 | } 82 | 83 | if data[k] >= data[j] { 84 | break 85 | } 86 | //下层 87 | data.swapAt(k, j) 88 | k = j 89 | } 90 | } 91 | 92 | 93 | 94 | 95 | 96 | 97 | } 98 | 99 | -------------------------------------------------------------------------------- /算法效率的度量.md: -------------------------------------------------------------------------------- 1 | ## 算法效率的度量 2 | 3 | 4 | > 作为一个iOS开发者,其实是很少用到算法的,但是算法是计算机运算的基础,虽然我们平时开发中很少使用,但是想要对软件开发有更加深入的研究,那么必须要了解一下算法,了解算法,我们可以知道程序的最佳运行效率,还可以知道我们程序是否高效 5 | 6 | 7 | 许多朋友其实是不知道我们写的程序的运行效率的(包括我,在今天之前也是不知道的),今天就让我们首先学习一下怎么知道我们程序的运行效率。 8 | 9 | ##### 算法效率的度量方法 10 | 11 | 我们常说设计算法要提高效率,但是我们怎么来知道我们的算法效率是多少呢,有两个最常见的方法 12 | - 1、利用记时功能,我们写几个算法,然后用计时器对不同的算法编程运行,进行时间比较,从而确定算法效率的高低 13 | 14 | 这个其实有一点不怎么靠谱,首先即使能够比较出来哪种算法比较快,但是我们还是不清楚为啥这种算法会快一点;其次现在CPU运行效率都比较快,如果是几百几千很有可能根本就区分不出来。所以这种方法基本排除了 15 | 16 | - 2、事前估算,在程序运行之前,运用统计的方法对算法进行估算,这种还是比较准确的 17 | 18 | 一个高级程序语言编写的程序在计算机运行所消耗的时间取决于一下几点 19 | 20 | - 1、算法采用的方法 21 | - 2、编译产生的代码量 22 | - 3、问题的输入规模 23 | - 4、机器执行指令的速度 24 | 25 | 第一条是算法好坏的根本,第二条由软件决定,第四条由硬件性能决定,抛开硬件与软件因素,决定一个算法效率的因素是算法采用的方法和问题的输入规模问题的输入规模 26 | 27 | 28 | 现在我们就举三个例子来说明一下问题 29 | ![E35F2694-9962-474B-9DB5-8BAA2493BE8E.png](http://upload-images.jianshu.io/upload_images/2348494-d2edc760992b696f.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 30 | 31 | 32 | 首先我们先看前两种算法,其实他们的效果都是一样的,都是计算从1加到100的值,但是第一种算法执行了`n+2`次,而第二种算法执行了3次。算法效率不用比都知道是哪一种的效率高了。 33 | 34 | 我们在来对三种算法进行比较,发现其实三种算法效率的高低主要是由核心也就是中间部分决定,我们去掉相同的部分,可以看到 35 | f(n)1 = n 36 | f(n)2 = 1 37 | f(n)3 = n*n 38 | 39 | 随着n的增大,他们之间的效率相差也是越大。 40 | 41 | 42 | ###### 到这里,我们来介绍一下这篇文章的核心概念`算法时间复杂度` 43 | 44 | > 概念:在进行算法分析的时候,语句总的执行次数T(n)是关于问题规模n的函数,进而分析T(n)随n的变化情况并确定T(n)的数量级。算法时间复杂度,也就是算法的时间度量,记着:`T(n) = O (f(n))`。它表示随问题规模n的增大,算法执行时间的增长率和f(n)的增长率相同,称作为算法渐进时间复杂度,简称时间复杂度 45 | 46 | 这样大写O`O()`来体现算法时间复杂度的记法,我们称作为`大O记法` 47 | 48 | 由此时间复杂度的记法的定义可知我们上面的三种算法的时间复杂度分别为`O(n) O(1) O(n* n)`。其中`O(1)`叫常数阶,`O(n)`叫线性阶,`O(n* n)`叫平方阶 49 | 50 | 51 | #### 推导大O记法 52 | - 1、用常数1来取代运行时间中所有的加法常数 53 | - 2、在修改后的运行次数函数中,只保留最高阶 54 | - 3、如果最高阶项存在但不是1,去除最高阶相乘的常数 55 | 56 | 57 | 一些时间复杂度解释 58 | ![EDFCCA98-3700-4EF2-8112-EB5E051A3C30.png](http://upload-images.jianshu.io/upload_images/2348494-080d1267c3fd9535.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 59 | 60 | 时间复杂度所消耗的时间排序 61 | ``` 62 | 0(1) Int { 17 | // 注意此时getSize的逻辑: 18 | // 如果tail >= front,非常简单,队列中的元素个数就是tail - front 19 | // 如果tail < front,说明我们的循环队列"循环"起来了,此时,队列中的元素个数为: 20 | // tail - front + data.count 21 | // 22 | // 也可以理解成,此时,data中没有元素的数目为front - tail, 23 | // 整体元素个数就是 data.count - (front - tail) = data.count + tail - front 24 | return tail >= front ? tail - front : tail - front + data.count; 25 | } 26 | //入队 27 | func enqueue(E:String) { 28 | //此时 队头 和 队尾 相同 我们需要对数组空间进行扩容,并且重新分配数组元素 29 | if (tail + 1) % size == front { 30 | //数组扩容 31 | resize() 32 | size = size * 2 33 | } 34 | //入队操作 35 | if data.count <= tail { 36 | data.append(E) 37 | }else{ 38 | data[tail] = E 39 | } 40 | 41 | //队尾计算 42 | tail = (tail + 1) % size 43 | 44 | } 45 | //出队 46 | func dequeue() -> String { 47 | let E = data[front] 48 | //把出队的数据占位 49 | data[front] = "nil" 50 | front = (front + 1) % size 51 | //数组缩容 52 | if data.count <= size / 4 53 | && data.count > 0{ 54 | resize() 55 | size = size / 2 56 | } 57 | 58 | return E 59 | } 60 | 61 | //数组调整 62 | func resize() { 63 | var newData = [String]() 64 | for (index,_) in data.enumerated() { 65 | let item = data[(index + front) % data.count] 66 | print(item) 67 | print((index + front) % data.count) 68 | if (item == "nil"){ 69 | continue 70 | } 71 | newData.append(item) 72 | } 73 | data = newData 74 | front = 0 75 | tail = newData.count 76 | } 77 | 78 | 79 | //打印 80 | func toString() -> String { 81 | var res = "LoopQueue: " 82 | for (index,item) in data.enumerated() { 83 | if (index < data.count - 1){ 84 | res.append("\(item),") 85 | } 86 | if (index == data.count - 1){ 87 | res.append("\(item)") 88 | } 89 | } 90 | res.append(" ->tail") 91 | return res 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /排序/希尔排序/希尔排序.md: -------------------------------------------------------------------------------- 1 | # 希尔排序 2 | 3 | 希尔排序(ShellSort)是以发明者Donald Shell名字命名的 4 | 5 | 希尔排序,也称递减增量排序算法,是插入排序的一种更高效的改进版本。但希尔排序是非稳定排序算法,对于中等数据的性能表现还不错。 6 | 7 | 8 | 希尔排序是基于插入排序的以下两点性质而提出改进方法的 9 | 10 | - 插入排序在对几乎已经排好序的数据操作时,效率高,即可以达到线性排序的效率 11 | - 但插入排序一般来说是低效的,因为插入排序每次只能将数据移动一位; 12 | 13 | 希尔排序的基本思想是:先将整个待排序的记录序列分割成为若干子序列分别进行直接插入排序,待整个序列中的记录"基本有序"时,再对全体记录进行依次直接插入排序。 14 | 15 | 16 | ### 逻辑 17 | 18 | 首先它把较大的数据集合分割成若干个小组(逻辑上分组),然后对每一个小组分别进行插入排序,此时,插入排序所作用的数据量比较小(每一个小组),插入的效率比较高 19 | 20 | ![](https://github.com/SunshineBrother/LeetCodeStudy/blob/master/算法/希尔排序/希尔排序1.jpeg) 21 | 22 | 23 | 可以看出,他是按下标相隔距离为4分的组,也就是说把下标相差4的分到一组,比如这个例子中a[0]与a[4]是一组、a[1]与a[5]是一组...,这里的差值(距离)被称为增量 24 | 25 | ![](https://github.com/SunshineBrother/LeetCodeStudy/blob/master/算法/希尔排序/希尔排序2.jpeg) 26 | 27 | 28 | 每个分组进行插入排序后,各个分组就变成了有序的了(整体不一定有序) 29 | 30 | ![](https://github.com/SunshineBrother/LeetCodeStudy/blob/master/算法/希尔排序/希尔排序3.jpeg) 31 | 32 | 33 | 此时,整个数组变的部分有序了(有序程度可能不是很高) 34 | 35 | ![](https://github.com/SunshineBrother/LeetCodeStudy/blob/master/算法/希尔排序/希尔排序4.jpeg) 36 | 37 | 38 | 然后缩小增量为上个增量的一半:2,继续划分分组,此时,每个分组元素个数多了,但是,数组变的部分有序了,插入排序效率同样比高 39 | 40 | ![](https://github.com/SunshineBrother/LeetCodeStudy/blob/master/算法/希尔排序/希尔排序5.jpeg) 41 | 42 | 同理对每个分组进行排序(插入排序),使其每个分组各自有序 43 | 44 | 45 | ![](https://github.com/SunshineBrother/LeetCodeStudy/blob/master/算法/希尔排序/希尔排序6.jpeg) 46 | 47 | 最后设置增量为上一个增量的一半:1,则整个数组被分为一组,此时,整个数组已经接近有序了,插入排序效率高 48 | 49 | ![](https://github.com/SunshineBrother/LeetCodeStudy/blob/master/算法/希尔排序/希尔排序7.jpeg) 50 | 51 | ![](https://github.com/SunshineBrother/LeetCodeStudy/blob/master/算法/希尔排序/希尔排序8.gif) 52 | 53 | 54 | 55 | ## 代码实现 56 | 57 | ``` 58 | class ShellSort: NSObject { 59 | private 60 | var data:[Int] = Array() 61 | 62 | func shellSort(data:[Int]) -> [Int] { 63 | self.data = data 64 | let incremental = data.count / 2 65 | recursiveShellSort(incremental: incremental) 66 | 67 | self.data = sorting(arr: self.data) 68 | return self.data 69 | } 70 | 71 | /// 递归 72 | /// 73 | /// - Parameter incremental: 增量 74 | private 75 | func recursiveShellSort(incremental:Int) { 76 | if incremental == 0 { 77 | return 78 | } 79 | for index in 0.. b{ 83 | data.swapAt(index, index+incremental) 84 | } 85 | 86 | } 87 | 88 | recursiveShellSort(incremental: incremental / 2) 89 | } 90 | 91 | //插入排序 92 | func sorting(arr:[Int]) -> Array{ 93 | var sortArr = arr 94 | for i in 0.. Array{ 26 | var sortArr:[Int] = arr 27 | for i in 0.. sortArr[j+1]{ 30 | sortArr.swapAt(j, j+1) 31 | } 32 | } 33 | } 34 | return sortArr 35 | } 36 | ``` 37 | 38 | ![](https://github.com/SunshineBrother/LeetCodeStudy/blob/master/算法/简单排序算法/冒泡排序.png) 39 | 40 | 我们查看打印发现,其实最后1次遍历其实是不用了,因为已经排序号结果了,但是因为我们没有进行判断,所以多运行了1次。我们的数据量比较小,所以多一次少一次没太大的性能差异,但是当数据量比较大的时候性能差异就显示出来了。为此我们继续进行优化 41 | 42 | 43 | ``` 44 | //冒泡排序 45 | func BubbleSorting1(arr:[Int]) -> Array{ 46 | var sortArr:[Int] = arr 47 | var swapped = false 48 | for i in 0.. sortArr[j+1]{ 52 | sortArr.swapAt(j, j+1) 53 | swapped = true 54 | } 55 | } 56 | if swapped == false{ 57 | break 58 | } 59 | } 60 | 61 | return sortArr 62 | } 63 | ``` 64 | 在判断语句中,如果一次都没有进行位置交换的话,证明已经排序完成了,这个时候可以停止遍历,结束循环 65 | 66 | ![](https://github.com/SunshineBrother/LeetCodeStudy/blob/master/算法/简单排序算法/冒泡排序1.png) 67 | 68 | 69 | ## 插入排序(Insertion Sort) 70 | 71 | 插入排序把数组分为已排序区和未排序区。取未排序区的元素,在已排序区上找到一个正确的位置插上去。还是希望对一个数据进行从小到大的排序。我们从未排序区上拿一个元素,按从右到左与已排序区的元素对比,如果如果当前元素 A 小于已排序区中的元素 B,让 B 往后移,即让 B 后面的位置等于 B,继续比 B 前面的数,也叫它为 B,它是新的一个 B,重复操作直到 A 大于 B,就让 A 插进当前 B 的前面。 72 | 73 | ``` 74 | func sorting(arr:[Int]) -> Array{ 75 | var sortArr = arr 76 | for i in 0.. Array{ 102 | var sortArr = arr 103 | let n = sortArr.count 104 | for i in 0.. sortArr[j]{ 109 | minIndex = j 110 | } 111 | } 112 | 113 | sortArr.swapAt(i, minIndex) 114 | 115 | } 116 | 117 | 118 | return sortArr 119 | } 120 | ``` 121 | 122 | 123 | -------------------------------------------------------------------------------- /排序/快速排序/快速排序.md: -------------------------------------------------------------------------------- 1 | # 快速排序 2 | 3 | 快速排序是由东尼·霍尔所发展的一种排序算法。在平均状况下,排序 n 个项目要 Ο(nlogn) 次比较。在最坏状况下则需要 Ο(n2) 次比较,但这种状况并不常见。事实上,快速排序通常明显比其他 Ο(nlogn) 算法更快,因为它的内部循环(inner loop)可以在大部分的架构上很有效率地被实现出来。 4 | 5 | 6 | 我们假设对4、6、2、3、1、5、7、8进行快速排序,我们在排序之前把数组分成两部分,一部分是大于4(第一个元素)的部分,一部分是小于4的部分。 7 | 我们假设对数组左边都是小于4的部分, 数组右边都是大于4的部分 8 | 9 | 10 | ![](https://github.com/SunshineBrother/LeetCodeStudy/blob/master/算法/快速排序/快速排序.png) 11 | 12 | 里面有两个步骤需要考虑,在大于4时和小于4时 13 | - 1、在大于4时,不做不处理 14 | - 2、在小于4的时候,我们需要把小于4的部分,挪移到右边数组中,也就是和第一个大于4的位置交换 15 | 16 | 17 | 18 | 19 | **代码实现** 20 | ``` 21 | private 22 | var arr:[Int] = Array() 23 | 24 | func quickSort(arr:[Int]) ->[Int]{ 25 | self.arr = arr 26 | recursiveQuickSort(l: 0, r: arr.count-1) 27 | return self.arr 28 | } 29 | 30 | //递归排序 31 | private 32 | func recursiveQuickSort(l:Int,r:Int) { 33 | if l >= r { 34 | return 35 | } 36 | 37 | let p = partition(l: l, r: r) 38 | recursiveQuickSort(l: l, r: p-1) 39 | recursiveQuickSort(l: p+1, r: r) 40 | } 41 | 42 | 43 | 44 | //对arr[l...r]部分进行partition 45 | //返回p,使得arr[l...p-1] < arr[p] arr[p+1...r] > arr[p] 46 | private 47 | func partition(l:Int,r:Int) -> Int { 48 | let v = arr[l] 49 | 50 | //arr[l+1...j] < v arr[j+1...i] > v 51 | var j = l 52 | for i in l+1.. Array { 36 | 37 | if rangR <= rangL{ 38 | fatalError("取值范围不准确") 39 | } 40 | 41 | var arr:[Int] = Array() 42 | for _ in 0.. Bool { 55 | for i in 0.. arr[i+1] { 57 | return false 58 | } 59 | } 60 | return true 61 | } 62 | ``` 63 | 64 | 65 | ![](https://github.com/SunshineBrother/LeetCodeStudy/blob/master/sort.png) 66 | 67 | 排序算法优越评价有三个指标,执行效率、内存消耗、稳定性,一般来讲,在分析效率时会从几个方面来衡量: 68 | - 时间复杂度。会从最好、最坏和平均情况三个来分析; 69 | - 时间复杂度的系数、常数 、低阶。在对同一阶时间复杂度的排序算法性能对比的时候,我们就要把系数、常数、低阶也考虑进来。 70 | - 比较次数和交换(或移动)次数。 71 | 72 | 73 | 74 | - [1、冒泡排序](https://github.com/SunshineBrother/LeetCodeStudy/blob/master/算法/冒泡排序/冒泡排序.md) 75 | - [2、选择排序](https://github.com/SunshineBrother/LeetCodeStudy/blob/master/算法/选择排序/选择排序.md) 76 | - [3、插入排序](https://github.com/SunshineBrother/LeetCodeStudy/blob/master/算法/插入排序/插入排序.md) 77 | - [4、希尔排序](https://github.com/SunshineBrother/LeetCodeStudy/blob/master/算法/希尔排序/希尔排序.md) 78 | - [5、归并排序](https://github.com/SunshineBrother/LeetCodeStudy/blob/master/算法/归并排序/归并排序.md) 79 | - [6、快速排序](https://github.com/SunshineBrother/LeetCodeStudy/blob/master/算法/快速排序/快速排序.md) 80 | - [7、堆排序](https://github.com/SunshineBrother/LeetCodeStudy/tree/master/算法/堆排序) 81 | - [8、计数排序](https://github.com/SunshineBrother/LeetCodeStudy/blob/master/算法/计数排序/计数排序.md) 82 | - [9、桶排序](https://github.com/SunshineBrother/LeetCodeStudy/blob/master/算法/桶排序/桶排序.md) 83 | - [10、基数排序](https://github.com/SunshineBrother/LeetCodeStudy/blob/master/算法/基数排序/基数排序.md) 84 | 85 | 86 | 87 | 88 | 排序算法参考: 89 | 90 | [十大经典排序算法](https://www.runoob.com/w3cnote/ten-sorting-algorithm.html) 91 | 92 | [五分钟学算法](https://mp.weixin.qq.com/s/vn3KiV-ez79FmbZ36SX9lg) 93 | 94 | 95 | 96 | 97 | -------------------------------------------------------------------------------- /数据结构/链表/链表.md: -------------------------------------------------------------------------------- 1 | ## 链表 2 | 3 | 我们上一章学习的,栈、队列,都是静态线性数据结构,底层依托于静态数组,依靠resize解决固定容量问题。 4 | 5 | 但是链表是真正的动态数据结构,也是最简单的动态数据结构,我们学习链表能够更加深入的理解指针,同时也能更加深入的理解递归函数。 6 | 7 | 链表的数组存储在节点(Node)中,节点通常有两部分组成,当节点数据为空的时候,就是最后一个节点 8 | ``` 9 | class Node { 10 | E e; //节点数据 11 | Node next; //指向下一个节点 12 | } 13 | ``` 14 | ![节点](https://github.com/SunshineBrother/LeetCodeStudy/blob/master/数据结构/链表/节点.png) 15 | 16 | - 优点:真正的动态,不需要处理固定容量问题 17 | - 缺点:丧失了随机访问的能力 18 | 19 | **数组和链表** 20 | - 数组最好用户索引有语义的情况 21 | - 最大的优点:支持快速查询 22 | 23 | - 链表不适合用于索引有语义的情况 24 | - 最大的优点:动态 25 | 26 | ### 创建链表 27 | 28 | 我们在为链表头部添加元素的时候,我们发现我们不知道第一个元素以前的节点是什么,这个时候为了更好更方便的处理,在链表中添加第一个元素,我们就创建一个虚拟的节点 29 | 30 | ![虚拟头节点](https://github.com/SunshineBrother/LeetCodeStudy/blob/master/数据结构/链表/虚拟头节点.png) 31 | 32 | ``` 33 | class LinkedList: NSObject { 34 | private var dummyHead:Node = Node() //虚拟头节点 35 | private var size:Int! = 0 //链表中的元素个数 36 | override init() { 37 | super.init() 38 | dummyHead.E = "" 39 | dummyHead.next = Node() 40 | } 41 | // 获取链表中的元素个数 42 | func getSize() -> Int { 43 | return size 44 | } 45 | //在链表中添加元素 46 | func add(index:Int,E:String) { 47 | var prev = dummyHead 48 | for _ in 0.. String { 64 | var cur = dummyHead 65 | for _ in 0..\(cur.E)") 96 | } 97 | print(res + "->NULL") 98 | } 99 | } 100 | 101 | class Node: NSObject { 102 | var E:String = "" //元素 103 | var next:Node! //指向下一个节点 104 | override init() { 105 | super.init() 106 | } 107 | 108 | init(E:String,next:Node) { 109 | self.E = E 110 | self.next = next 111 | } 112 | 113 | } 114 | ``` 115 | ### Leetcode 203. 移除链表元素 116 | 117 | 删除链表中等于给定值 val 的所有节点。 118 | 119 | 示例: 120 | ``` 121 | 输入: 1->2->6->3->4->5->6, val = 6 122 | 输出: 1->2->3->4->5 123 | ``` 124 | 125 | **不使用虚拟头节点** 126 | ``` 127 | func removeElements(_ head: ListNode?, _ val: Int) -> ListNode? { 128 | var headNode = head 129 | //判断第一个节点的val值是否是需要移除的那一个 130 | while headNode != nil && headNode?.val == val { 131 | headNode = headNode?.next 132 | } 133 | //第一个节点判断结束以后如果,链表为空,直接return 134 | if headNode == nil { 135 | return headNode 136 | } 137 | //判断剩余链表 138 | var prev:ListNode = headNode! 139 | while prev.next != nil { 140 | if prev.next?.val == val{ 141 | //下一个节点如果是要去除的值,去掉下一个节点值 142 | //把当前节点的下一个节点的值 修改为 当前节点下下一个节点的值,这个步骤是去掉下一个节点的值 143 | prev.next = prev.next?.next 144 | }else{ 145 | //遍历下一个节点 146 | prev = prev.next! 147 | } 148 | } 149 | 150 | return headNode 151 | } 152 | ``` 153 | 154 | 155 | **使用虚拟头节点** 156 | 157 | ``` 158 | //如果要是使用虚拟头结点的话 159 | func removeElements1(_ head: ListNode?, _ val: Int) -> ListNode? { 160 | let dummyHead = ListNode(-1) 161 | dummyHead.next = head 162 | var prev:ListNode = dummyHead 163 | while prev.next != nil { 164 | if prev.next?.val == val{ 165 | //下一个节点如果是要去除的值,去掉下一个节点值 166 | //把当前节点的下一个节点的值 修改为 当前节点下下一个节点的值,这个步骤是去掉下一个节点的值 167 | prev.next = prev.next?.next 168 | }else{ 169 | //遍历下一个节点 170 | prev = prev.next! 171 | } 172 | } 173 | 174 | 175 | return dummyHead.next 176 | } 177 | ``` 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | -------------------------------------------------------------------------------- /数据结构/平衡二叉树/AVL/AVL/AVLTree.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AVLTree.swift 3 | // AVL 4 | // 5 | // Created by yunna on 2019/5/27. 6 | // Copyright © 2019年 yunna. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | class Node: NSObject { 12 | var value:String = "" 13 | var leftNode = Node() 14 | var rightNode = Node() 15 | var height:Int = 0 16 | 17 | init(value:String) { 18 | self.value = value 19 | } 20 | override init() { 21 | super.init() 22 | } 23 | 24 | } 25 | 26 | 27 | class AVLTree: NSObject { 28 | 29 | private 30 | var rootNode:Node! 31 | 32 | // 获得节点node的高度 33 | func getHeight(node:Node?) -> Int { 34 | guard node == nil else { 35 | return 0 36 | } 37 | return node?.height ?? 0 38 | } 39 | // 获得节点node的平衡因子 40 | func getBalanceFactor(node:Node?) -> Int { 41 | guard node == nil else { 42 | return 0 43 | } 44 | 45 | let left = getHeight(node: node!.leftNode) 46 | let right = getHeight(node: node!.rightNode) 47 | return left - right 48 | } 49 | 50 | // 向二分搜索树中添加新的元素(key, value) 51 | func add(value:String) { 52 | rootNode = recursiveAdd(node: rootNode, value: value) 53 | } 54 | 55 | // 向以node为根的二分搜索树中插入元素value,递归算法 56 | // 返回插入新节点后二分搜索树的根 57 | func recursiveAdd(node:Node?,value:String) -> Node{ 58 | if node == nil { 59 | return Node(value: value) 60 | } 61 | 62 | //二分搜索添加元素 63 | if value.compare(node!.value).rawValue < 0{ 64 | node?.leftNode = recursiveAdd(node: node?.leftNode, value: value) 65 | }else if value.compare(node!.value).rawValue > 0{ 66 | node?.rightNode = recursiveAdd(node: node?.rightNode,value: value) 67 | }else{ 68 | node?.value = value 69 | } 70 | 71 | // 更新height 72 | node?.height = 1 + abs(getHeight(node: node?.leftNode) - getHeight(node: node?.rightNode)) 73 | 74 | // 计算平衡因子 75 | let balanceFactor = getBalanceFactor(node: node) 76 | 77 | //平衡维护 78 | if (balanceFactor > 1 && getBalanceFactor(node: node?.leftNode) >= 0){ 79 | return rightRotate(y: node!) 80 | }else if (balanceFactor < -1 && getBalanceFactor(node: node?.rightNode) <= 0){ 81 | return leftRotate(y: node!) 82 | }else if (balanceFactor > 1 && getBalanceFactor(node: node?.leftNode) < 0){ 83 | node!.leftNode = leftRotate(y: node!.leftNode) 84 | return rightRotate(y: node!) 85 | }else if (balanceFactor < -1 && getBalanceFactor(node: node?.leftNode) > 0){ 86 | node?.rightNode = rightRotate(y: node!.rightNode); 87 | return leftRotate(y: node!); 88 | } 89 | 90 | return node! 91 | } 92 | // 对节点y进行向右旋转操作,返回旋转后新的根节点x 93 | // y x 94 | // / \ / \ 95 | // x T4 向右旋转 (y) z y 96 | // / \ - - - - - - - -> / \ / \ 97 | // z T3 T1 T2 T3 T4 98 | // / \ 99 | // T1 T2 100 | 101 | func rightRotate(y:Node) -> Node { 102 | let x = y.leftNode 103 | let t3 = x.rightNode 104 | 105 | //向右旋转过程 106 | x.rightNode = y 107 | y.leftNode = t3 108 | 109 | //更新height 110 | y.height = 1 + abs(getHeight(node: y.leftNode) - getHeight(node: y.rightNode)) 111 | x.height = 1 + abs(getHeight(node: x.leftNode) - getHeight(node: x.rightNode)) 112 | return x 113 | } 114 | 115 | 116 | 117 | 118 | // 对节点y进行向左旋转操作,返回旋转后新的根节点x 119 | // y x 120 | // / \ / \ 121 | // T1 x 向左旋转 (y) y z 122 | // / \ - - - - - - - -> / \ / \ 123 | // T2 z T1 T2 T3 T4 124 | // / \ 125 | // T3 T4 126 | 127 | func leftRotate(y:Node) -> Node { 128 | let x = y.rightNode 129 | let t2 = x.leftNode 130 | 131 | //向左旋转 132 | x.leftNode = y 133 | y.rightNode = t2 134 | 135 | //更新height 136 | y.height = 1 + abs(getHeight(node: y.leftNode) - getHeight(node: y.rightNode)) 137 | x.height = 1 + abs(getHeight(node: x.leftNode) - getHeight(node: x.rightNode)) 138 | 139 | return x 140 | } 141 | 142 | 143 | 144 | 145 | 146 | } 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | -------------------------------------------------------------------------------- /数据结构/线段树/线段树/线段树/SegmentTree.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SegmentTree.swift 3 | // 线段树 4 | // 5 | // Created by yunna on 2019/5/9. 6 | // Copyright © 2019年 yunna. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | class SegmentTree: NSObject { 12 | 13 | private var data:[Int] = Array() //外界传过来的数组 14 | private var tree:[Int] = Array() //线段树 15 | private let merger = Merger() //区间合并方法 16 | 17 | // 返回完全二叉树的数组表示中,一个索引所表示的元素的左孩子节点的索引 18 | func leftChild(index:Int) -> Int { 19 | return index * 2 + 1 20 | } 21 | //返回完全二叉树的数组表示中,一个索引所表示的元素的右孩子节点的索引 22 | func rightChild(index:Int) -> Int { 23 | return index * 2 + 2 24 | } 25 | 26 | //创建tree 27 | func SegmentTree(data:[Int]) { 28 | self.data = data 29 | for _ in 0..<4*data.count { 30 | tree.append(0) 31 | } 32 | buildSegmentTree(treeIndex: 0, l: 0, R: data.count - 1) 33 | } 34 | 35 | // 在treeIndex的位置创建表示区间[l...r]的线段树 36 | private 37 | func buildSegmentTree(treeIndex:Int,l:Int,R:Int) { 38 | if l == R { 39 | tree[treeIndex] = data[l] 40 | return 41 | } 42 | // treeIndex的节点分为[l...mid]和[mid+1...r]两部分 43 | let leftTreeIndex = leftChild(index: treeIndex) 44 | let rightTreeIndex = rightChild(index: treeIndex) 45 | let mid = (l + R) / 2 46 | //左侧递归 47 | buildSegmentTree(treeIndex: leftTreeIndex 48 | , l: l, R: mid) 49 | //右侧递归 50 | buildSegmentTree(treeIndex: rightTreeIndex, l: mid+1, R: R) 51 | tree[treeIndex] = merger.merger(a: tree[leftTreeIndex], b: tree[rightTreeIndex]) 52 | } 53 | 54 | // 返回区间[queryL, queryR]的值 55 | func query(queryL:Int,queryR:Int) -> Int { 56 | if queryL < 0 || 57 | queryR >= data.count || 58 | queryR < 0 || 59 | queryR >= data.count || 60 | queryR < queryL{ 61 | fatalError("Index is illegal") 62 | } 63 | 64 | return recursiveQuery(treeIndex: 0, L: 0, R: data.count-1, queryL: queryL, queryR: queryR) 65 | } 66 | // 在以treeIndex为根的线段树中[l...r]的范围里,搜索区间[queryL...queryR]的值 67 | private 68 | func recursiveQuery(treeIndex:Int,L:Int,R:Int,queryL:Int,queryR:Int) -> Int { 69 | if L == queryL && 70 | R == queryR{ 71 | return tree[treeIndex] 72 | } 73 | // treeIndex的节点分为[l...mid]和[mid+1...r]两部分 74 | let leftTreeIndex = leftChild(index: treeIndex) 75 | let rightTreeIndex = rightChild(index: treeIndex) 76 | let mid = (L + R) / 2 77 | 78 | //确定[queryL...queryR]的区间位置 79 | if queryL >= mid + 1 { 80 | let result = recursiveQuery(treeIndex: rightTreeIndex, L: mid + 1, R: R, queryL: queryL, queryR: queryR) 81 | return result 82 | }else if queryR <= mid{ 83 | let result = recursiveQuery(treeIndex: leftTreeIndex, L: L, R: mid, queryL: queryL, queryR: queryR) 84 | return result 85 | } 86 | 87 | //求和操作 88 | let leftResult = recursiveQuery(treeIndex: leftTreeIndex, L: L, R: mid, queryL: queryL 89 | , queryR: mid) 90 | let rightResult = recursiveQuery(treeIndex: rightTreeIndex, L: mid + 1, R: R, queryL: mid + 1, queryR: queryR) 91 | 92 | return merger.merger(a: leftResult, b: rightResult) 93 | } 94 | 95 | //更新操作 96 | func update(index:Int,E:Int){ 97 | if index < 0 && 98 | index >= data.count{ 99 | fatalError("Index is illegal") 100 | } 101 | 102 | data[index] = E 103 | recursiveUpdate(treeIndex: 0, l: 0, r: data.count-1, E: E) 104 | } 105 | // 在以treeIndex为根的线段树中更新index的值为e 106 | private 107 | func recursiveUpdate(treeIndex:Int,l:Int,r:Int,E:Int) { 108 | if r == l { 109 | tree[treeIndex] = E; 110 | return 111 | } 112 | 113 | // treeIndex的节点分为[l...mid]和[mid+1...r]两部分 114 | let leftTreeIndex = leftChild(index: treeIndex) 115 | let rightTreeIndex = rightChild(index: treeIndex) 116 | let mid = (l + r) / 2 117 | if treeIndex >= mid + 1 { 118 | recursiveUpdate(treeIndex: rightTreeIndex, l: mid+1, r: r, E: E) 119 | }else{ 120 | recursiveUpdate(treeIndex: leftTreeIndex, l: l, r: mid, E: E) 121 | } 122 | 123 | tree[treeIndex] = merger.merger(a: tree[leftTreeIndex], b: tree[rightTreeIndex]) 124 | } 125 | 126 | 127 | } 128 | 129 | 130 | 131 | 132 | class Merger: NSObject { 133 | func merger(a:Int,b:Int) -> Int{ 134 | return a + b 135 | } 136 | } 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | -------------------------------------------------------------------------------- /数据结构/优先队列/优先队列.md: -------------------------------------------------------------------------------- 1 | ## 优先队列 2 | 3 | 队列可以分为两类 4 | 5 | - 普通队列:先进先出,后进后出 6 | - 优先队列:出队顺序和入队顺序无关,和优先级有关 7 | 8 | 我们来看一下优先队列的几种结构的算法效率 9 | 10 | | |入队|出队(拿出最大元素)| 11 | |---|:---:|:---:| 12 | |普通线性结构|O(1)|O(n)| 13 | |顺序线性结构|O(n)|O(1)| 14 | |堆|O(logn)|O(logn)| 15 | 16 | - 普通线性结构:在入队的时候我们不需要处理,所以算法效率是O(1),但是在出队的时候,我们需要拿出优先级最高的元素,需要全部遍历一遍,算法效率是O(n) 17 | - 顺序线性结构:我们在入队的时候需要找出优先级最高的元素,算法效率是O(n),出队的时候,直接拿出第一位就可以了,算法效率是O(1) 18 | - 堆:使用堆不论是入队还是出队,算法效率都是O(logn) 19 | 20 | 21 | ![算法效率](https://github.com/SunshineBrother/LeetCodeStudy/blob/master/数据结构/优先队列/算法效率.png) 22 | 23 | 24 | ### 二叉堆 25 | 26 | **满二叉树** 27 | 28 | 满二叉树:所有的结点都存在左子树和右子树,并且所有叶子都在同一层上 29 | 30 | ![满二叉树](https://github.com/SunshineBrother/LeetCodeStudy/blob/master/数据结构/优先队列/满二叉树.png) 31 | 32 | **完全二叉树** 33 | 34 | 完全二叉树:对于一个树高为h的二叉树,如果其第0层至第h-1层的节点都满。如果最下面一层节点不满,则所有的节点在左边的连续排列,空位都在右边。这样的二叉树就是一棵完全二叉树。 35 | 36 | ![完全二叉树](https://github.com/SunshineBrother/LeetCodeStudy/blob/master/数据结构/优先队列/完全二叉树.png) 37 | 38 | 39 | **二叉堆** 40 | 41 | 最大堆:堆中某个节点的值总是不大于其父节点的值 42 | 43 | 相应的我们也可以定义最小堆 44 | 45 | 46 | ![二叉堆](https://github.com/SunshineBrother/LeetCodeStudy/blob/master/数据结构/优先队列/二叉堆.png) 47 | 48 | 这个时候我们可以使用一个线性结构表示 49 | 50 | ![线性结构](https://github.com/SunshineBrother/LeetCodeStudy/blob/master/数据结构/优先队列/线性结构.png) 51 | 52 | 对于此时,我们可以根据做一些表示 53 | 54 | ``` 55 | parent(i) = i / 2 //查找父节点 56 | left child(i) = 2 * i //查找左子树 57 | right child(i) = 2 * i + 1 //查找又子树 58 | ``` 59 | 60 | 61 | ### 实现 62 | 63 | 现在我们就简单的实现一些功能 64 | 65 | - `size`:返回堆中的元素个数 66 | - `isEmpty`:返回一个布尔值, 表示堆中是否为空 67 | - `parent`:返回完全二叉树的数组表示中,一个索引所表示的元素的父亲节点的索引 68 | - `leftChild`:返回完全二叉树的数组表示中,一个索引所表示的元素的左孩子节点的索引 69 | - `rightChild`:返回完全二叉树的数组表示中,一个索引所表示的元素的右孩子节点的索引 70 | - `add`:向堆中添加元素 71 | - `remove`:移除元素 72 | - `replace`:取出最大元素后,放入一个新的元素 73 | 74 | 这里我们以索引为0的时候为最大元素值 75 | 76 | ``` 77 | class MaxHeap: NSObject { 78 | private var data:[Int] = Array() //存放堆的数组 79 | 80 | // 返回堆中的元素个数 81 | func size() -> Int { 82 | return data.count 83 | } 84 | 85 | // 返回一个布尔值, 表示堆中是否为空 86 | func isEmpty() -> Bool { 87 | return data.isEmpty 88 | } 89 | 90 | // 返回完全二叉树的数组表示中,一个索引所表示的元素的父亲节点的索引 91 | func parent(index:Int) -> Int { 92 | if index == 0 { 93 | fatalError("index-0 doesn't have parent.") 94 | } 95 | return (index - 1) / 2; 96 | } 97 | 98 | // 返回完全二叉树的数组表示中,一个索引所表示的元素的左孩子节点的索引 99 | func leftChild(index:Int) -> Int { 100 | return index * 2 + 1 101 | } 102 | //返回完全二叉树的数组表示中,一个索引所表示的元素的右孩子节点的索引 103 | func rightChild(index:Int) -> Int { 104 | return index * 2 + 2 105 | } 106 | } 107 | ``` 108 | 109 | **添加元素** 110 | 111 | 添加元素的时候 112 | - 1、我们先把添加的元素放到数组的末尾 113 | - 2、最大二叉堆的定义,我们判断子节点是否大于父节点,如果子节点大于父节点的话,子节点和父节点交换位置,然后继续向上遍历,知道子节点小于等于父节点,停止遍历 114 | 115 | 116 | ![siftUp](https://github.com/SunshineBrother/LeetCodeStudy/blob/master/数据结构/优先队列/siftUp.png) 117 | 118 | 实现代码 119 | ``` 120 | //添加元素 121 | func add(child:Int) { 122 | data.append(child) 123 | siftUp(i: data.count - 1) 124 | } 125 | //上浮过程 126 | func siftUp(i:Int) { 127 | var child = i //当前节点索引 128 | while (i > 0) && (data[child] > data[self.parent(index: child)]) { 129 | //交换child 和 parent 130 | data.swapAt(self.parent(index: child), child) 131 | //索引上浮 132 | child = self.parent(index: child) 133 | } 134 | 135 | } 136 | ``` 137 | 我们这里来进行验证一下 138 | 139 | ``` 140 | let heap = MaxHeap() 141 | let data = [62,41,30,28,16,22,13,19,17,15,52] 142 | for (_,item) in data.enumerated(){ 143 | heap.add(child: item) 144 | } 145 | print(heap.data) 146 | ``` 147 | 148 | ![siftUp打印](https://github.com/SunshineBrother/LeetCodeStudy/blob/master/数据结构/优先队列/siftUp打印.png) 149 | 150 | 我们对比上面我们画的图,发现打印结果跟我们原来预想的一致。 151 | 152 | 153 | 154 | **移除元素** 155 | 156 | 移除元素的时候跟上面添加元素又点类似 157 | 158 | - 1、先移除线性表索引为0的元素 159 | - 2、把最后一位元素放到线性表索引为0的位置 160 | - 3、索引为0的元素和左右子树,对比,最大的子树上移,重复上面步骤,知道大于等于最大子树的时候停止 161 | 162 | 163 | ![siftUp](https://github.com/SunshineBrother/LeetCodeStudy/blob/master/数据结构/优先队列/siftDown.png) 164 | 165 | ``` 166 | //删除元素 167 | func remove() -> Int { 168 | let last = data[data.count - 1] 169 | //索引为0的位置和最大的位置交换 170 | data.swapAt(0, data.count-1) 171 | //移除最后一位 172 | data.removeLast() 173 | //下沉 174 | siftDown(i: 0) 175 | 176 | return last 177 | } 178 | //下沉过程 179 | private 180 | func siftDown(i:Int) { 181 | var k = i 182 | while leftChild(index: k) < data.count { 183 | //j 是左右孩子中最大值 184 | var j = leftChild(index: k) 185 | if (j+1 < data.count) && (data[j+1] > data[j]){ 186 | j += 1 187 | } 188 | 189 | if data[k] >= data[j] { 190 | break 191 | } 192 | //下层 193 | data.swapAt(k, j) 194 | k = j 195 | } 196 | } 197 | ``` 198 | 199 | **replace** 200 | 201 | `replace`:取出最大元素后,放入一个新的元素,这里我们有两种思路 202 | - 1、先取出最大元素,然后在添加新的元素,两次O(logn)操作 203 | - 2、直接将堆顶元素替换,然后下层操作,一次O(logn)操作 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | -------------------------------------------------------------------------------- /数据结构/Trie/Trie.md: -------------------------------------------------------------------------------- 1 | ## Trie 2 | 3 | 4 | 在计算机科学中,Trie,又称字典树、单词查找树或键树,是一种树形结构,是一种哈希树的变种。 5 | 6 | 典型应用是用于统计,排序和保存大量的字符串(但不仅限于字符串),所以经常被搜索引擎系统用于文本词频统计。它的优点是:利用字符串的公共前缀来减少查询时间,最大限度地减少无谓的字符串比较,查询效率比哈希树高。 7 | 8 | 9 | Trie查询每个条目的时间复杂度和字典中一共有多少条目有关,时间复杂度为O(w),w为查询单词的长度 10 | 11 | ![](https://github.com/SunshineBrother/LeetCodeStudy/blob/master/数据结构/Trie/Trie1.png) 12 | 13 | 每个节点都有26个指向下个节点的指针 14 | ``` 15 | class Node{ 16 | char c; 17 | Node next[26]; 18 | } 19 | ``` 20 | 21 | 22 | 若是不考虑语言,不同的情景,每个节点都有若干指向下个节点的指针 23 | 24 | ``` 25 | class Node{ 26 | char c; 27 | Mapnext; 28 | } 29 | ``` 30 | 31 | 32 | ![](https://github.com/SunshineBrother/LeetCodeStudy/blob/master/数据结构/Trie/Trie2.png) 33 | 34 | 如上最右侧树,`pan`和`panda`都是一个单词,但是他们在一个子树上面,所以我们还要有一个`Bool`值来记录是否是一个完整的单词 35 | 36 | ``` 37 | Class Node{ 38 | bool isWord; 39 | Mapnext; 40 | } 41 | ``` 42 | 43 | **代码实现** 44 | 45 | 创建Node节点 46 | ``` 47 | class Node: NSObject { 48 | var isWord:Bool = false 49 | var next:[Character:Node] = Dictionary() 50 | } 51 | ``` 52 | 53 | 创建字典树类 54 | ``` 55 | class TrieMap: NSObject { 56 | private var root = Node() //节点 57 | private var size = 0 //存储的单词量 58 | } 59 | ``` 60 | 61 | 向trie中添加一个单词 62 | ``` 63 | //向trie中添加一个单词 64 | func add(word:String) { 65 | var cur = root 66 | for item in word { 67 | //如果下个节点不包含,添加 68 | if !cur.next.keys.contains(item){ 69 | let node = Node() 70 | cur.next[item] = node 71 | } 72 | //找到下一个节点 73 | cur = cur.next[item] ?? Node() 74 | } 75 | 76 | //如果已经存在这个单词了,不处理 77 | //如果不存在这个单词,size++,isWord设置为true 78 | if !cur.isWord { 79 | cur.isWord = true 80 | size += 1 81 | } 82 | } 83 | ``` 84 | 85 | 查询单词word是否在Trie中 86 | 87 | ``` 88 | func contains(word:String) -> Bool { 89 | var cur = root 90 | for item in word { 91 | //如果下个节点不包含 92 | if !cur.next.keys.contains(item){ 93 | return false 94 | } 95 | //找到下一个节点 96 | cur = cur.next[item] ?? Node() 97 | } 98 | 99 | //即使已经word全部字符都在字典树中,我们也要看看最后一个isWord是否是一个true,对比`pan`和`panda` 100 | return cur.isWord 101 | } 102 | ``` 103 | 104 | 105 | 查询是否在Trie中有单词以prefix为前缀 106 | ``` 107 | // 查询是否在Trie中有单词以prefix为前缀 108 | func isPrefix(word:String) -> Bool { 109 | var cur = root 110 | for item in word { 111 | //如果下个节点不包含 112 | if !cur.next.keys.contains(item){ 113 | return false 114 | } 115 | //找到下一个节点 116 | cur = cur.next[item] ?? Node() 117 | } 118 | return true 119 | } 120 | ``` 121 | 122 | 123 | 124 | 125 | ### LeetCode 126 | 127 | **208、实现 Trie (前缀树)** 128 | 129 | 实现一个 Trie (前缀树),包含 `insert`, `search`, 和 `startsWith` 这三个操作。 130 | 131 | 示例: 132 | ``` 133 | Trie trie = new Trie(); 134 | 135 | trie.insert("apple"); 136 | trie.search("apple"); // 返回 true 137 | trie.search("app"); // 返回 false 138 | trie.startsWith("app"); // 返回 true 139 | trie.insert("app"); 140 | trie.search("app"); // 返回 true 141 | ``` 142 | 143 | 说明: 144 | - 你可以假设所有的输入都是由小写字母 a-z 构成的。 145 | - 保证所有输入均为非空字符串。 146 | 147 | 148 | 这道题的解法,我们可以直接复制上面我们的视线就可以了。 149 | 150 | 151 | **211、 添加与搜索单词 - 数据结构设计** 152 | 153 | 设计一个支持以下两种操作的数据结构: 154 | ``` 155 | void addWord(word) 156 | bool search(word) 157 | ``` 158 | 159 | search(word) 可以搜索文字或正则表达式字符串,字符串只包含字母 . 或 a-z 。 . 可以表示任何一个字母。 160 | 161 | 示例: 162 | 163 | ``` 164 | addWord("bad") 165 | addWord("dad") 166 | addWord("mad") 167 | search("pad") -> false 168 | search("bad") -> true 169 | search(".ad") -> true 170 | search("b..") -> true 171 | ``` 172 | 173 | 说明: 174 | 你可以假设所有单词都是由小写字母 a-z 组成的。 175 | 176 | 177 | 178 | ``` 179 | class leetCode211: NSObject { 180 | private var root = Node() //节点 181 | 182 | func addWord(_ word: String) { 183 | var cur = root 184 | for item in word { 185 | //如果下个节点不包含,添加 186 | if !cur.next.keys.contains(item){ 187 | let node = Node() 188 | cur.next[item] = node 189 | } 190 | //找到下一个节点 191 | cur = cur.next[item] ?? Node() 192 | } 193 | 194 | //如果已经存在这个单词了,不处理 195 | //如果不存在这个单词,size++,isWord设置为true 196 | if !cur.isWord { 197 | cur.isWord = true 198 | } 199 | } 200 | 201 | func search(_ word: String) -> Bool { 202 | let result = recursiveSearch(word: word, cur: root, index: 0) 203 | return result 204 | } 205 | 206 | func recursiveSearch(word: String,cur:Node,index:Int) -> Bool { 207 | if index == word.count { 208 | return cur.isWord 209 | } 210 | 211 | let c:Character = word.first! 212 | let a:Character = "." 213 | if c != a { 214 | if !cur.next.keys.contains(c){ 215 | return false 216 | } 217 | 218 | return recursiveSearch(word: word, cur: cur.next[c]!, index: index+1) 219 | }else{ 220 | for key in cur.next.keys{ 221 | if(recursiveSearch(word: word, cur: cur.next[key]!, index: index+1)){ 222 | return true 223 | } 224 | } 225 | return false 226 | } 227 | 228 | } 229 | 230 | } 231 | 232 | 233 | ``` 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | -------------------------------------------------------------------------------- /数据结构/哈希表/哈希表.md: -------------------------------------------------------------------------------- 1 | ## 哈希表 2 | 3 | 哈希表(`hash table`,也叫散列表),是根据键(`key`)直接访问访问在内存储存位置的数据结构。 4 | 5 | 哈希表本质是一个数组,数组中的每一个元素成为一个箱子,箱子中存放的是键值对。根据下标`index`从数组中取`value`。关键是如何获取`index`,这就需要一个固定的`函数(哈希函数)`,将`key`转换成`index`。不论哈希函数设计的如何完美,都可能出现不同的`key`经过`hash`处理后得到相同的`hash`值,这时候就需要处理哈希冲突。 6 | 7 | **哈希表特点** 8 | - 优点 :哈希表可以提供快速的操作 9 | - 缺点 :哈希表通常是基于数组的,数组创建后难于扩展。 也没有一种简便的方法可以以任何一种顺序〔例如从小到大)遍历表中的数据项。 10 | 11 | 综上,如果不需要有序遍历数据,井且可以提前预测数据量的大小。那么哈希表在速度和易用性方面是无与伦比的。 12 | 13 | 14 | **哈希查找步骤** 15 | 16 | - 1、使用哈希函数将被查找的键映射(转换)为数组的索引,理想情况下(hash函数设计合理)不同的键映射的数组下标也不同,所有的查找时间复杂度为O(1)。但是实际情况下不是这样的,所以哈希查找的第二步就是处理哈希冲突。 17 | - 2、处理哈希碰撞冲突。处理方法有很多,比如拉链法、线性探测法。 18 | 19 | 20 | **哈希表存储过程** 21 | - 1、使用hash函数根据key得到哈希值h 22 | - 2、如果箱子的个数为n,那么值应该存放在底(h%n)个箱子中。h%n的值范围为[0, n-1]。 23 | - 3、如果该箱子非空(已经存放了一个值)即不同的key得到了相同的h产生了哈希冲突,此时需要使用拉链法或者开放定址线性探测法解决冲突。 24 | 25 | 26 | **哈希函数** 27 | 28 | 29 | 哈希查找第一步就是使用哈希函数将键映射成索引。这种映射函数就是哈希函数。如果我们有一个保存0-M数组,那么我们就需要一个能够将任意键转换为该数组范围内的索引(0~M-1)的哈希函数。哈希函数需要易于计算并且能够均匀分布所有键。比如举个简单的例子,使用手机号码后三位就比前三位作为key更好,因为前三位手机号码的重复率很高。再比如使用身份证号码出生年月位数要比使用前几位数要更好。 30 | 在实际中,我们的键并不都是数字,有可能是字符串,还有可能是几个值的组合等,所以我们需要实现自己的哈希函数。 31 | 32 | - 1、直接寻址法 33 | - 2、数字分析法 34 | - 3、平方取中法 35 | - 4、折叠法 36 | - 5、随机数法 37 | - 6、除留余数法 38 | 39 | 优秀的哈希函数需要满足一下特点 40 | - 从哈希值不能反向推导出原始数据(所以哈希算法也叫单向哈希算法) 41 | - 对输入数据非常敏感,哪怕原始数据只修改了一个 Bit,最后得到的哈希值也大不相同 42 | - 散列冲突的概率要很小,对于不同的原始数据,哈希值相同的概率非常小 43 | - 哈希算法的执行效率要尽量高效,针对较长的文本,也能快速地计算出哈希值 44 | 45 | 46 | 47 | 48 | 49 | **哈希表大小** 50 | 51 | 在设计用除法来散射的哈希表时,我们都会用数值模哈希表大小,得到的余数来作为ID存入哈希表对应格子中。所有文章都表明要用一个较大的素数来作为哈希表的大小,也就是要模一个较大的素数。但为什么就是要用素数呢?简单分析一下可以看出玄机 52 | 53 | 先看看如果用一个合数8作为哈希表大小,0-30在哈希表中的散射情况 54 | 55 | ![](https://github.com/SunshineBrother/LeetCodeStudy/blob/master/数据结构/哈希表/哈希表大小1.png) 56 | 57 | 58 | 再来看看用质数7作为哈希表大小,0-30在哈希表中的散射情况: 59 | ![](https://github.com/SunshineBrother/LeetCodeStudy/blob/master/数据结构/哈希表/哈希表大小2.png) 60 | 61 | 62 | 我们都知道,合数8除了1和自身以外,还有2跟4这两个因数。观察表1的单独一列可以发现,这些在同一列的数,他们实际上就是上一个数+8,而查看2、4、6这三行我们发现,因为2 4 6 能被2(或4)整除,而在同一列上的数在+8以后一样满足可以被2(或4)整除的这一特性。例如4这一列,4、12、20、28,这些哈希映射在同一个格子里的数都是前一个数+8,然后他们都能被2和4整除,这样就导致他们之间有很强烈的关系,很容易发生哈希冲突。 63 | 64 | 再来看看表2,同样情况,同一列中的数都是由上一个数+7得到的,但因为7是一个素数,它除了1跟本身之外没有其他因数,所以在同一列的数里就找不到我们刚刚所说的那种特性。 65 | 66 | 而我们都知道,哈希表设计目的就是希望尽量的随机散射,不希望这些在同一列上的元素(也就是会冲突的元素)之间具有关系,所以我们都采用素数作为哈希表的大小,从而避免模数相同的数之间具备公共因数 67 | 68 | 我们这里给出一些质数 69 | ![](https://github.com/SunshineBrother/LeetCodeStudy/blob/master/数据结构/哈希表/哈希表大小3.png) 70 | 71 | 72 | [更多的可以参考这里](https://planetmath.org/goodhashtableprimes) 73 | 74 | 75 | **负载因子** 76 | 77 | 负载因子是哈希表的一个重要属性,用来衡量哈希表的空/满程度,一定程度也可以提现查询的效率。负载因子越大,意味着哈希表越满,越容易导致冲突,性能也就越低。所以当负载因子大于某个常数(一般是0.75)时,哈希表将自动扩容。哈希表扩容时,一般会创建两倍于原来的数组长度。因此即使key的哈希值没有变化,对数组个数取余的结果会随着数组个数的扩容发生变化,因此键值对的位置都有可能发生变化,这个过程也成为重哈希(`rehash`)。 78 | 79 | 80 | **哈希表扩容** 在数组比较多的时候,需要重新哈希并移动数据,性能影响较大。 81 | 82 | 哈希表扩容 虽然能够使负载因子降低,但并不总是能有效提高哈希表的查询性能。比如哈希函数设计的不合理,导致所有的key计算出的哈希值都相同,那么即使扩容他们的位置还是在同一条链表上,变成了线性表,性能极低,查询的时候时间复杂度就变成了O(n) 83 | 84 | 85 | ### 哈希冲突的解决方法 86 | 87 | 88 | #### 1、拉链法 89 | 90 | 91 | 简单来说就是 数组 + 链表 。将键通过hash函数映射为大小为M的数组的下标索引,数组的每一个元素指向一个链表,链表中的每一个结点存储着hash出来的索引值为结点下标的键值对。 92 | 93 | 94 | `Java 8`解决哈希冲突采用的就是拉链法。在处理哈希函数设计不合理导致链表很长时(`链表长度超过8切换为红黑树,小于6重新退化为链表`)。将链表切换为红黑树能够保证插入和查找的效率,缺点是当哈希表比较大时,哈希表扩容会导致瞬时效率降低。 95 | 96 | `Redis`解决哈希冲突采用的也是拉链法。通过增量式扩容解决了Java 8中的瞬时扩容导致的瞬时效率降低的缺点,同时拉链法的实现方式(新插入的键值对放在链表头部)带来了两个好处: 97 | 98 | - 一、头插法可以节省插入耗时。如果插到尾部,则需要时间复杂度为O(n)的操作找到链表尾部,或者需要额外的内存地址来保存尾部链表的位置 99 | - 二、头插法可以节省查找耗时。对于一个数据系统来说,最新插入的数据往往可能频繁的被查询 100 | 101 | 102 | #### 2、开放定址线性探测发 103 | 104 | 使用两个大小为N的数组(一个存放keys,另一个存放values)。使用数组中的空位解决碰撞,当碰撞发生时(即一个键的hash值对应数组的下标被两外一个键占用)直接将下标索引加一(index += 1),这样会出现三种结果: 105 | - 1、未命中(数组下标中的值为空,没有占用)。keys[index] = key,values[index] = value。 106 | - 2、命中(数组下标中的值不为空,占用)。keys[index] == key,values[index] == value。 107 | - 3、命中(数组下标中的值不为空,占用)。keys[index] != key,继续index += 1,直到遇到结果1或2停止。 108 | 109 | 110 | **缺点** 111 | - 1、容易产生堆积问题 112 | - 2、不适于大规模的数据存储 113 | - 3、散列函数的设计对冲突会有很大的影响 114 | - 4、插入时可能会出现多次冲突的现象,删除的元素是多个冲突元素中的一个,需要对后面的元素作处理,实现较复杂 115 | - 5、结点规模很大时会浪费很多空间 116 | 117 | 118 | 119 | 120 | ### Hash表的平均查找长度 121 | 122 | `Hash表的平均查找长度包括查找成功时的平均查找长度和查找失败时的平均查找长度。` 123 | 124 | 查找成功时的平均查找长度=表中每个元素查找成功时的比较次数之和/表中元素个数; 125 | 126 | 查找不成功时的平均查找长度相当于在表中查找元素不成功时的平均比较次数,可以理解为向表中插入某个元素,该元素在每个位置都有可能,然后计算出在每个位置能够插入时需要比较的次数,再除以表长即为查找不成功时的平均查找长度。 127 | 128 | 129 | > 给定一组数据{32,14,23,01,42,20,45,27,55,24,10,53},假设散列表的长度为13(最接近n的质数),散列函数为H(k) = k%13。分别画出用 线性探测法 和 拉链法 解决冲突时构造的哈希表,并求出在等概率下情况,这两种方法查找成功和查找不成功的平均查找长度 130 | 131 | **拉链法** 132 | 133 | ![](https://github.com/SunshineBrother/LeetCodeStudy/blob/master/数据结构/哈希表/拉链法.png) 134 | 135 | **查找成功时的平均查找长度** 136 | 137 | ``` 138 | ASL = (1*6+2*4+3*1+4*1)/12 = 7/4 139 | ``` 140 | **查找不成功时的平均查找长度** 141 | 142 | ``` 143 | ASL = (4+2+2+1+2+1)/13 144 | ``` 145 | 146 | 147 | **线性探测法** 148 | 149 | ![](https://github.com/SunshineBrother/LeetCodeStudy/blob/master/数据结构/哈希表/线性探测法.png) 150 | 151 | 152 | 查找成功时查找次数=插入元素时的比较次数,查找成功的平均查找长度: 153 | 154 | ``` 155 | ASL = (1+2+1+4+3+1+1+1+3+9+1+1+3)/12=2.5 156 | ``` 157 | 158 | 查找不成功时的查找次数=第n个位置不成功时的比较次数为,第n个位置到第1个没有数据位置的距离:如第0个位置取值为1,第1个位置取值为2.查找不成功时的平均查找长度: 159 | 160 | 161 | ``` 162 | ASL = (1+2+3+4+5+6+7+8+9+10+11+12)/ 13 = 91/13 163 | ``` 164 | 165 | 166 | [转载自:搞iOS的,面试官问Hash干嘛?原因远比我下面要介绍的多](https://juejin.im/post/5c6abfc86fb9a049c04396a7) 167 | 168 | [转载自:为什么求模运算要用素数(质数)—— 哈希表设计](https://blog.csdn.net/wangchong_fly/article/details/47442265) 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | -------------------------------------------------------------------------------- /数据结构/平衡二叉树/平衡二叉树.md: -------------------------------------------------------------------------------- 1 | ## 平衡二叉树 2 | 3 | > 平衡二叉搜索树(Self-balancing binary search tree)又被称为AVL树(有别于AVL算法),且具有以下性质:它是一 棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树 4 | 5 | **特点** 6 | - 1、对于任意节点,左子树和右子树的高度差不能超过1 7 | - 2、平衡二叉树的高度和节点数量之间的关系是O(logn) 8 | 9 | 我们在写一个平衡二叉树的时候为了维护他的特性,我们需要注意两个东西 10 | - 1、标准节点高度 11 | - 2、计算平衡因子:计算平衡因子就是左右子树高度差 12 | 13 | ![](https://github.com/SunshineBrother/LeetCodeStudy/blob/master/数据结构/平衡二叉树/AVL1.png) 14 | 15 | 16 | **为什么要使用平衡二叉树** 17 | 18 | 我们来使用一个二分搜索树来简单的阐明一下 19 | 20 | 二分搜索树的特点 21 | - 1、大于其左子树的所有节点的值 22 | - 2、小于其右子树的所有节点的值 23 | 24 | 一般情况下二分搜索树的算法效率:O(n)-O(logn)。在极端情况下二分搜索树会退化为O(n)效率 25 | 26 | ![](https://github.com/SunshineBrother/LeetCodeStudy/blob/master/数据结构/平衡二叉树/AVL2.png) 27 | 28 | 29 | 此时的算法效率就退化到了O(n)效率,为了解决这个问题我们可以使用平衡二叉树来实现二分搜索树 30 | 31 | ### 代码实现 32 | 33 | 34 | ### 节点 35 | ``` 36 | class Node: NSObject { 37 | var key:String = "" 38 | var value:String = "" 39 | var leftNode = Node() 40 | var rightNode = Node() 41 | var height:Int = 0 42 | } 43 | ``` 44 | 45 | ### 获得节点node的高度 46 | ``` 47 | func getHeight(node:Node?) -> Int { 48 | guard node == nil else { 49 | return 0 50 | } 51 | return node?.height ?? 0 52 | } 53 | ``` 54 | 55 | ### 获得节点node的平衡因子 56 | ``` 57 | // 获得节点node的平衡因子 58 | func getBalanceFactor(node:Node?) -> Int { 59 | guard node == nil else { 60 | return 0 61 | } 62 | 63 | let left = getHeight(node: node!.leftNode) 64 | let right = getHeight(node: node!.rightNode) 65 | return left - right 66 | } 67 | 68 | ``` 69 | 70 | 71 | ### 添加一个节点 72 | 73 | 在什么时候我们需要维护一个平衡 74 | - 在加入一个节点以后,沿着节点向上维护平衡性 75 | 76 | 77 | **右旋转RR** 78 | 79 | 80 | ![](https://github.com/SunshineBrother/LeetCodeStudy/blob/master/数据结构/平衡二叉树/AVL3.png) 81 | 82 | 对于这样一个树,在左侧添加一个子节点以后,打破了平衡二叉树 83 | 此时 84 | - 1、t1 < z < t2 < x < t3 < y < t4 85 | - 2、我们假设`z`的子节点的高度为`h`,`z`的高度为`h+1`;`t+3`的高度为`h或者h+1`;`x`的高度为`h+2`;`t4`的高度为`h`;因为是刚好打破平衡,所有此时y的高度为`h+3`,计算平衡因子为`2` 86 | 87 | 88 | 89 | 我们对y节点进行右旋转 90 | ![](https://github.com/SunshineBrother/LeetCodeStudy/blob/master/数据结构/平衡二叉树/AVL4.png) 91 | 92 | 此时 93 | ``` 94 | x.right = y 95 | y.left = t3 96 | ``` 97 | 98 | 99 | ``` 100 | // 对节点y进行向右旋转操作,返回旋转后新的根节点x 101 | // y x 102 | // / \ / \ 103 | // x T4 向右旋转 (y) z y 104 | // / \ - - - - - - - -> / \ / \ 105 | // z T3 T1 T2 T3 T4 106 | // / \ 107 | // T1 T2 108 | func rightRotate(y:Node) -> Node { 109 | let x = y.leftNode 110 | let t3 = x.rightNode 111 | 112 | //向右旋转过程 113 | x.rightNode = y 114 | y.leftNode = t3 115 | 116 | //更新height 117 | y.height = 1 + abs(getHeight(node: y.leftNode) - getHeight(node: y.rightNode)) 118 | x.height = 1 + abs(getHeight(node: x.leftNode) - getHeight(node: x.rightNode)) 119 | return x 120 | } 121 | ``` 122 | **左旋转LL** 123 | 124 | ![](https://github.com/SunshineBrother/LeetCodeStudy/blob/master/数据结构/平衡二叉树/AVL5.png) 125 | 126 | 左旋转跟右旋转类似 127 | 128 | ``` 129 | x.left = y 130 | y.right = t3 131 | ``` 132 | 133 | ``` 134 | // 对节点y进行向左旋转操作,返回旋转后新的根节点x 135 | // y x 136 | // / \ / \ 137 | // T1 x 向左旋转 (y) y z 138 | // / \ - - - - - - - -> / \ / \ 139 | // T2 z T1 T2 T3 T4 140 | // / \ 141 | // T3 T4 142 | 143 | func leftRotate(y:Node) -> Node { 144 | let x = y.rightNode 145 | let t2 = x.leftNode 146 | 147 | //向左旋转 148 | x.leftNode = y 149 | y.rightNode = t2 150 | 151 | //更新height 152 | y.height = 1 + abs(getHeight(node: y.leftNode) - getHeight(node: y.rightNode)) 153 | x.height = 1 + abs(getHeight(node: x.leftNode) - getHeight(node: x.rightNode)) 154 | 155 | return x 156 | } 157 | ``` 158 | **先左再右LR** 159 | 160 | ![](https://github.com/SunshineBrother/LeetCodeStudy/blob/master/数据结构/平衡二叉树/AVL6.png) 161 | 162 | **先右再左RL** 163 | 164 | 跟上面的差不多 165 | 166 | 167 | 代码实现 168 | ``` 169 | // 向二分搜索树中添加新的元素(key, value) 170 | func add(value:String) { 171 | rootNode = recursiveAdd(node: rootNode, value: value) 172 | } 173 | 174 | // 向以node为根的二分搜索树中插入元素value,递归算法 175 | // 返回插入新节点后二分搜索树的根 176 | func recursiveAdd(node:Node?,value:String) -> Node{ 177 | if node == nil { 178 | return Node(value: value) 179 | } 180 | 181 | //二分搜索添加元素 182 | if value.compare(node!.value).rawValue < 0{ 183 | node?.leftNode = recursiveAdd(node: node?.leftNode, value: value) 184 | }else if value.compare(node!.value).rawValue > 0{ 185 | node?.rightNode = recursiveAdd(node: node?.rightNode,value: value) 186 | }else{ 187 | node?.value = value 188 | } 189 | 190 | // 更新height 191 | node?.height = 1 + abs(getHeight(node: node?.leftNode) - getHeight(node: node?.rightNode)) 192 | 193 | // 计算平衡因子 194 | let balanceFactor = getBalanceFactor(node: node) 195 | 196 | //平衡维护 197 | if (balanceFactor > 1 && getBalanceFactor(node: node?.leftNode) >= 0){ 198 | return rightRotate(y: node!) 199 | }else if (balanceFactor < -1 && getBalanceFactor(node: node?.rightNode) <= 0){ 200 | return leftRotate(y: node!) 201 | }else if (balanceFactor > 1 && getBalanceFactor(node: node?.leftNode) < 0){ 202 | node!.leftNode = leftRotate(y: node!.leftNode) 203 | return rightRotate(y: node!) 204 | }else if (balanceFactor < -1 && getBalanceFactor(node: node?.leftNode) > 0){ 205 | node?.rightNode = rightRotate(y: node!.rightNode); 206 | return leftRotate(y: node!); 207 | } 208 | 209 | return node! 210 | } 211 | ``` 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | -------------------------------------------------------------------------------- /README的副本.md: -------------------------------------------------------------------------------- 1 | # LeetCodeStudy 2 | 3 | 数据结构和算法的学习 4 | 5 | 6 | ## 工具 7 | - [如何直观形象地树状打印一棵二叉树](https://github.com/SunshineBrother/LeetCodeStudy/blob/master/数据结构/打印树/如何直观形象地树状打印一棵二叉树?.md) 8 | 9 | ## 数据结构 10 | 11 | - [1、算法效率的度量](https://github.com/SunshineBrother/LeetCodeStudy/blob/master/算法效率的度量.md) 12 | - [2、栈和队列](https://github.com/SunshineBrother/LeetCodeStudy/blob/master/数据结构/栈和队列/栈和队列.md) 13 | - [3、链表](https://github.com/SunshineBrother/LeetCodeStudy/blob/master/数据结构/链表/链表.md) 14 | - [4、递归](https://github.com/SunshineBrother/LeetCodeStudy/blob/master/数据结构/递归/递归.md) 15 | - [5、二叉树](https://github.com/SunshineBrother/LeetCodeStudy/blob/master/数据结构/二叉树/二叉树.md) 16 | - [6、集合和映射](https://github.com/SunshineBrother/LeetCodeStudy/blob/master/数据结构/集合和映射/集合和映射.md) 17 | - [7、优先队列](https://github.com/SunshineBrother/LeetCodeStudy/blob/master/数据结构/优先队列/优先队列.md) 18 | - [8、线段树](https://github.com/SunshineBrother/LeetCodeStudy/blob/master/数据结构/线段树/线段树.md) 19 | - [9、Trie](https://github.com/SunshineBrother/LeetCodeStudy/blob/master/数据结构/Trie/Trie.md) 20 | - [10、并查集](https://github.com/SunshineBrother/LeetCodeStudy/blob/master/数据结构/并查集/并查集.md) 21 | - [11、平衡二叉树](https://github.com/SunshineBrother/LeetCodeStudy/blob/master/数据结构/平衡二叉树/平衡二叉树.md) 22 | - [12、哈希表](https://github.com/SunshineBrother/LeetCodeStudy/blob/master/数据结构/哈希表/哈希表.md) 23 | 24 | 25 | 26 | 27 | *********************************************** 28 | 29 | ## 算法 30 | 31 | 在开始之前我们先来写一个帮助测试的函数 32 | ``` 33 | // 生成有n个元素的随机数组,每个元素的随机范围为[rangeL, rangeR] 34 | 35 | func generateRandomArray(count:Int,rangL:Int,rangR:Int) -> Array { 36 | 37 | if rangR <= rangL{ 38 | fatalError("取值范围不准确") 39 | } 40 | 41 | var arr:[Int] = Array() 42 | for _ in 0.. Bool { 55 | for i in 0.. arr[i+1] { 57 | return false 58 | } 59 | } 60 | return true 61 | } 62 | ``` 63 | 64 | 65 | ![](https://github.com/SunshineBrother/LeetCodeStudy/blob/master/sort.png) 66 | 67 | 排序算法优越评价有三个指标,执行效率、内存消耗、稳定性,一般来讲,在分析效率时会从几个方面来衡量: 68 | - 时间复杂度。会从最好、最坏和平均情况三个来分析; 69 | - 时间复杂度的系数、常数 、低阶。在对同一阶时间复杂度的排序算法性能对比的时候,我们就要把系数、常数、低阶也考虑进来。 70 | - 比较次数和交换(或移动)次数。 71 | 72 | 73 | 74 | - [1、冒泡排序](https://github.com/SunshineBrother/LeetCodeStudy/blob/master/算法/冒泡排序/冒泡排序.md) 75 | - [2、选择排序](https://github.com/SunshineBrother/LeetCodeStudy/blob/master/算法/选择排序/选择排序.md) 76 | - [3、插入排序](https://github.com/SunshineBrother/LeetCodeStudy/blob/master/算法/插入排序/插入排序.md) 77 | - [4、希尔排序](https://github.com/SunshineBrother/LeetCodeStudy/blob/master/算法/希尔排序/希尔排序.md) 78 | - [5、归并排序](https://github.com/SunshineBrother/LeetCodeStudy/blob/master/算法/归并排序/归并排序.md) 79 | - [6、快速排序](https://github.com/SunshineBrother/LeetCodeStudy/blob/master/算法/快速排序/快速排序.md) 80 | - [7、堆排序](https://github.com/SunshineBrother/LeetCodeStudy/tree/master/算法/堆排序) 81 | - [8、计数排序](https://github.com/SunshineBrother/LeetCodeStudy/blob/master/算法/计数排序/计数排序.md) 82 | - [9、桶排序](https://github.com/SunshineBrother/LeetCodeStudy/blob/master/算法/桶排序/桶排序.md) 83 | - [10、基数排序](https://github.com/SunshineBrother/LeetCodeStudy/blob/master/算法/基数排序/基数排序.md) 84 | 85 | 86 | 87 | 88 | 排序算法参考: 89 | 90 | [十大经典排序算法](https://www.runoob.com/w3cnote/ten-sorting-algorithm.html) 91 | 92 | [五分钟学算法](https://mp.weixin.qq.com/s/vn3KiV-ez79FmbZ36SX9lg) 93 | 94 | 95 | 96 | 97 | 98 | ## 数据结构和算法必知必会的50个代码实现 99 | 100 | - [算法面试](https://github.com/labuladong/fucking-algorithm) 101 | 102 | 103 | ### 数组 104 | *************************************** 105 | - 1、实现一个支持动态扩容的数组 106 | - 2、实现一个大小固定的有序数组,支持动态增删改操作 107 | - 3、实现两个有序数组合并为一个有序数组 108 | 109 | 110 | ### 链表 111 | 112 | *************************************** 113 | 114 | - 1、实现单链表、循环链表、双向链表,支持增删操作 115 | - 2、实现单链表反转 116 | - 3、实现两个有序的链表合并为一个有序链表 117 | - 4、实现求链表的中间结点 118 | 119 | 120 | ### 栈 121 | 122 | *************************************** 123 | - 1、用数组实现一个顺序栈 124 | - 2、用链表实现一个链式栈 125 | - 3、编程模拟实现一个浏览器的前进、后退功能 126 | 127 | 128 | ### 队列 129 | 130 | *************************************** 131 | - 1、用数组实现一个顺序队列 132 | - 2、用链表实现一个链式队列 133 | - 3、实现一个循环队列 134 | 135 | ### 递归 136 | 137 | *************************************** 138 | - 1、编程实现斐波那契数列求值f(n)=f(n-1)+f(n-2) 139 | - 2、编程实现求阶乘n! 140 | - 3、编程实现一组数据集合的全排列 141 | 142 | ### 排序 143 | 144 | *************************************** 145 | - 1、实现归并排序、快速排序、插入排序、冒泡排序、选择排序 146 | - 2、编程实现O(n)时间复杂度内找到一组数据的第K大元素 147 | 148 | 149 | ### 二分查找 150 | 151 | *************************************** 152 | 153 | - 1、实现一个有序数组的二分查找算法 154 | - 2、实现模糊二分查找算法(比如大于等于给定值的第一个元素) 155 | 156 | 157 | ### 散列表 158 | 159 | *************************************** 160 | - 1、实现一个基于链表法解决冲突问题的散列表 161 | - 2、实现一个LRU缓存淘汰算法 162 | 163 | 164 | ### 字符串 165 | 166 | *************************************** 167 | 168 | - 1、实现一个字符集,只包含a~z这26个英文字母的Trie树 169 | - 2、实现朴素的字符串匹配算法 170 | 171 | 172 | ### 二叉树 173 | 174 | *************************************** 175 | - 1、实现一个二叉查找树,并且支持插入、删除、查找操作 176 | - 2、实现查找二叉查找树中某个节点的后继、前驱节点 177 | - 3、实现二叉树前、中、后序以及按层遍历 178 | 179 | 180 | 181 | ### 堆 182 | 183 | *************************************** 184 | - 1、实现一个小顶堆、大顶堆、优先级队列 185 | - 2、实现堆排序 186 | - 3、利用优先级队列合并K个有序数组 187 | - 4、求一组动态数据集合的最大Top K 188 | 189 | ### 图 190 | 191 | *************************************** 192 | - 1、实现有向图、无向图、有权图、无权图的邻接矩阵和邻接表表示方法 193 | - 2、实现图的深度优先搜索、广度优先搜索 194 | - 3、实现Dijkstra算法、A*算法 195 | - 4、实现拓扑排序的Kahn算法、DFS算法 196 | 197 | 198 | 199 | ### 回溯 200 | 201 | *************************************** 202 | - 1、利用回溯算法求解八皇后问题 203 | - 2、利用回溯算法求解0-1背包问题 204 | 205 | 206 | 207 | ### 分治 208 | 209 | *************************************** 210 | - 1、利用分治算法求一组数据的逆序对个数 211 | 212 | 213 | ### 动态规划 214 | 215 | *************************************** 216 | - 1、0-1背包问题 217 | - 2、最小路径和 218 | - 3、编程实现莱文斯坦最短编辑距离 219 | - 4、编程实现查找两个字符串的最长公共子序列 220 | - 5、编程实现一个数据序列的最长递增子序列 221 | -------------------------------------------------------------------------------- /数据结构/线段树/线段树.md: -------------------------------------------------------------------------------- 1 | ## 线段树 2 | 3 | 线段树是一种二叉搜索树,它将一个区间划分成一些单元区间,每个单元区间对应线段树中的一个叶结点 4 | 5 | **使用** 6 | 7 | 对于给定的区间 8 | - 1、更新:更新区间中一个元素或者一个区间的值 9 | - 2、查询:查询区间[i,j]的最大值,最小值,或者区间数字和 10 | 11 | ![线段树1](https://github.com/SunshineBrother/LeetCodeStudy/blob/master/数据结构/线段树/线段树1.png) 12 | 13 | 14 | ![线段树2](https://github.com/SunshineBrother/LeetCodeStudy/blob/master/数据结构/线段树/线段树2.png) 15 | 16 | 17 | 如果区间有N个元素,数组便是需要多少节点 18 | 19 | 0层:1 20 | 1层:2 21 | 2层:4 22 | 3层:8 23 | h-1层:2*(h-1) 24 | 25 | 26 | 对于满二叉树 27 | h层一共有2*h-1个节点,大约是2*h个节点 28 | 29 | 但是不是满二叉树的时候,会多出一层,最后一层的元素个数,大概是前面所有层的加和 30 | 需要4*n个节点 31 | 32 | `所以:如果有区间有n个元素,数组表示需要多少节点,那么最大开辟4*n个节点就够用了` 33 | 34 | **代码实现** 35 | 对于代码我们主要实现以下几点 36 | - 1、创建线段树`SegmentTree` 37 | - 2、查询`query` 38 | - 3、更新`update` 39 | 40 | ``` 41 | class SegmentTree: NSObject { 42 | 43 | var data:[Int] = Array() //外界传过来的数组 44 | private var tree:[Int] = Array() //线段树 45 | private let merger = Merger() //区间合并方法 46 | 47 | // 返回完全二叉树的数组表示中,一个索引所表示的元素的左孩子节点的索引 48 | func leftChild(index:Int) -> Int { 49 | return index * 2 + 1 50 | } 51 | //返回完全二叉树的数组表示中,一个索引所表示的元素的右孩子节点的索引 52 | func rightChild(index:Int) -> Int { 53 | return index * 2 + 2 54 | } 55 | 56 | //创建tree 57 | func SegmentTree() { 58 | for _ in 0..<4*data.count { 59 | tree.append(0) 60 | } 61 | buildSegmentTree(treeIndex: 0, l: 0, R: data.count - 1) 62 | } 63 | 64 | // 在treeIndex的位置创建表示区间[l...r]的线段树 65 | private 66 | func buildSegmentTree(treeIndex:Int,l:Int,R:Int) { 67 | if l == R { 68 | tree[treeIndex] = data[l] 69 | return 70 | } 71 | // treeIndex的节点分为[l...mid]和[mid+1...r]两部分 72 | let leftTreeIndex = leftChild(index: treeIndex) 73 | let rightTreeIndex = rightChild(index: treeIndex) 74 | let mid = (l + R) / 2 75 | //左侧递归 76 | buildSegmentTree(treeIndex: leftTreeIndex 77 | , l: l, R: mid) 78 | //右侧递归 79 | buildSegmentTree(treeIndex: rightTreeIndex, l: mid+1, R: R) 80 | tree[treeIndex] = merger.merger(a: tree[leftTreeIndex], b: tree[rightTreeIndex]) 81 | } 82 | 83 | // 返回区间[queryL, queryR]的值 84 | func query(queryL:Int,queryR:Int) -> Int { 85 | if queryL < 0 || 86 | queryR >= data.count || 87 | queryR < 0 || 88 | queryR >= data.count || 89 | queryR < queryL{ 90 | fatalError("Index is illegal") 91 | } 92 | 93 | return recursiveQuery(treeIndex: 0, L: 0, R: data.count-1, queryL: queryL, queryR: queryR) 94 | } 95 | // 在以treeIndex为根的线段树中[l...r]的范围里,搜索区间[queryL...queryR]的值 96 | private 97 | func recursiveQuery(treeIndex:Int,L:Int,R:Int,queryL:Int,queryR:Int) -> Int { 98 | if L == queryL && 99 | R == queryR{ 100 | return tree[treeIndex] 101 | } 102 | // treeIndex的节点分为[l...mid]和[mid+1...r]两部分 103 | let leftTreeIndex = leftChild(index: treeIndex) 104 | let rightTreeIndex = rightChild(index: treeIndex) 105 | let mid = (L + R) / 2 106 | 107 | //确定[queryL...queryR]的区间位置 108 | if queryL >= mid + 1 { 109 | let result = recursiveQuery(treeIndex: rightTreeIndex, L: mid + 1, R: R, queryL: queryL, queryR: queryR) 110 | return result 111 | }else if queryR <= mid{ 112 | let result = recursiveQuery(treeIndex: leftTreeIndex, L: L, R: mid, queryL: queryL, queryR: queryR) 113 | return result 114 | } 115 | 116 | //求和操作 117 | let leftResult = recursiveQuery(treeIndex: leftTreeIndex, L: L, R: mid, queryL: queryL 118 | , queryR: mid) 119 | let rightResult = recursiveQuery(treeIndex: rightTreeIndex, L: mid + 1, R: R, queryL: mid + 1, queryR: queryR) 120 | 121 | return merger.merger(a: leftResult, b: rightResult) 122 | } 123 | 124 | //更新操作 125 | func update(index:Int,E:Int){ 126 | if index < 0 && 127 | index >= data.count{ 128 | fatalError("Index is illegal") 129 | } 130 | 131 | data[index] = E 132 | recursiveUpdate(treeIndex: 0, l: 0, r: data.count-1, E: E) 133 | } 134 | // 在以treeIndex为根的线段树中更新index的值为e 135 | private 136 | func recursiveUpdate(treeIndex:Int,l:Int,r:Int,E:Int) { 137 | if r == l { 138 | tree[treeIndex] = E; 139 | return 140 | } 141 | 142 | // treeIndex的节点分为[l...mid]和[mid+1...r]两部分 143 | let leftTreeIndex = leftChild(index: treeIndex) 144 | let rightTreeIndex = rightChild(index: treeIndex) 145 | let mid = (l + r) / 2 146 | if treeIndex >= mid + 1 { 147 | recursiveUpdate(treeIndex: rightTreeIndex, l: mid+1, r: r, E: E) 148 | }else{ 149 | recursiveUpdate(treeIndex: leftTreeIndex, l: l, r: mid, E: E) 150 | } 151 | 152 | tree[treeIndex] = merger.merger(a: tree[leftTreeIndex], b: tree[rightTreeIndex]) 153 | } 154 | 155 | 156 | } 157 | 158 | 159 | 160 | 161 | class Merger: NSObject { 162 | func merger(a:Int,b:Int) -> Int{ 163 | return a + b 164 | } 165 | } 166 | 167 | 168 | ``` 169 | 170 | 171 | `merger`区间合并方法,我们可以定义具体的实现方法 172 | 173 | 174 | **303. 区域和检索 - 数组不可变** 175 | 176 | 给定一个整数数组 nums,求出数组从索引 i 到 j (i ≤ j) 范围内元素的总和,包含 i, j 两点。 177 | 178 | 示例: 179 | 180 | ``` 181 | 给定 nums = [-2, 0, 3, -5, 2, -1],求和函数为 sumRange() 182 | 183 | sumRange(0, 2) -> 1 184 | sumRange(2, 5) -> -1 185 | sumRange(0, 5) -> -3 186 | ``` 187 | 188 | 说明: 189 | 190 | 你可以假设数组不可变。 191 | 会多次调用 sumRange 方法。 192 | 193 | 194 | ``` 195 | class NumArray { 196 | 197 | private 198 | var sum:[Int] = Array() 199 | init(_ nums: [Int]) { 200 | var numArr = nums 201 | numArr.append(0) 202 | sum.append(0) 203 | for index in 1.. Int { 210 | return sum[j+1] - sum[i]; 211 | } 212 | } 213 | 214 | 215 | 216 | 217 | class NumArray1 { 218 | 219 | private 220 | var segment = SegmentTree() 221 | 222 | init(_ nums: [Int]) { 223 | segment.SegmentTree(data: nums) 224 | } 225 | 226 | func sumRange(_ i: Int, _ j: Int) -> Int { 227 | let result = segment.query(queryL: i, queryR: j) 228 | return result 229 | } 230 | } 231 | ``` 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | -------------------------------------------------------------------------------- /数据结构/并查集/并查集.md: -------------------------------------------------------------------------------- 1 | ## 并查集 2 | 3 | 并查集,在一些有N个元素的集合应用问题中,我们通常是在开始时让每个元素构成一个单元素的集合,然后按一定顺序将属于同一组的元素所在的集合合并,其间要反复查找一个元素在哪个集合中。这一类问题近几年来反复出现在信息学的国际国内赛题中,其特点是看似并不复杂,但数据量极大,若用正常的数据结构来描述的话,往往在空间上过大,计算机无法承受;即使在空间上勉强通过,运行的时间复杂度也极高,根本就不可能在比赛规定的运行时间(1~3秒)内计算出试题需要的结果,只能用并查集来描述。 4 | 5 | 并查集是一种树型的数据结构,用于处理一些不相交集合(Disjoint Sets)的合并及查询问题。常常在使用中以森林来表示。 6 | 7 | 8 | **案例** 9 | 10 | 首先在地图上给你若干个城镇,这些城镇都可以看作点,然后告诉你哪些对城镇之间是有道路直接相连的。最后要解决的是整幅图的连通性问题。比如随意给你两个点,让你判断它们是否连通,或者问你整幅图一共有几个连通分支,也就是被分成了几个互相独立的块。像畅通工程这题,问还需要修几条路,实质就是求有几个连通分支。如果是1个连通分支,说明整幅图上的点都连起来了,不用再修路了;如果是2个连通分支,则只要再修1条路,从两个分支中各选一个点,把它们连起来,那么所有的点都是连起来的了;如果是3个连通分支,则只要再修两条路…… 11 | 12 | 13 | 下面讲个一个小例子帮助理解 14 | 15 | 江湖上散落着各式各样的大侠,有上千个之多。他们没有什么正当职业,整天背着剑在外面走来走去,碰到和自己不是一路人的,就免不了要打一架。但大侠们有一个优点就是讲义气,绝对不打自己的朋友。而且他们信奉“朋友的朋友就是我的朋友”,只要是能通过朋友关系串联起来的,不管拐了多少个弯,都认为是自己人。这样一来,江湖上就形成了一个一个的帮派,通过两两之间的朋友关系串联起来。而不在同一个帮派的人,无论如何都无法通过朋友关系连起来,于是就可以放心往死了打。但是两个原本互不相识的人,如何判断是否属于一个朋友圈呢? 16 | 17 | 18 | 我们可以在每个朋友圈内推举出一个比较有名望的人,作为该圈子的代表人物。这样,每个圈子就可以这样命名“中国同胞队”美国同胞队”……两人只要互相对一下自己的队长是不是同一个人,就可以确定敌友关系了。 19 | 20 | 21 | 但是还有问题啊,大侠们只知道自己直接的朋友是谁,很多人压根就不认识队长抓狂要判断自己的队长是谁,只能漫无目的的通过朋友的朋友关系问下去:“你是不是队长?你是不是队长?”这样,想打一架得先问个几十年,饿都饿死了,受不了。这样一来,队长面子上也挂不住了,不仅效率太低,还有可能陷入无限循环中。于是队长下令,重新组队。队内所有人实行分等级制度,形成树状结构,我队长就是根节点,下面分别是二级队员、三级队员。每个人只要记住自己的上级是谁就行了。遇到判断敌友的时候,只要一层层向上问,直到最高层,就可以在短时间内确定队长是谁了。由于我们关心的只是两个人之间是否是一个帮派的,至于他们是如何通过朋友关系相关联的,以及每个圈子内部的结构是怎样的,甚至队长是谁,都不重要了。所以我们可以放任队长随意重新组队,只要不搞错敌友关系就好了。于是,门派产生了 22 | 23 | 24 | ![](https://github.com/SunshineBrother/LeetCodeStudy/blob/master/数据结构/并查集/并查集.png) 25 | 26 | 27 | [内容转载自:并查集详解(超级简单有趣~~就学会了)](https://blog.csdn.net/qq_41593380/article/details/81146850) 28 | 29 | 30 | ### 代码实现 31 | 并查集主要是要实现一下两个方法 32 | - 1、isConnected(int p, int q) 两个元素是否有链接 33 | - 2、unionElements(int p, int q) 合并两个元素 34 | 35 | 我们使用元素都是数组的简单实现一下 36 | 37 | 38 | 39 | **数组实现** 40 | 41 | ``` 42 | // 我们的第一版Union-Find本质就是一个数组 43 | private var ids:[Int] = Array() 44 | init(size:Int) { 45 | super.init() 46 | // 初始化, 每一个id[i]指向自己, 没有合并的元素 47 | for item in 0.. Int { 55 | if p >= ids.count || p < 0 { 56 | fatalError("p is out of bound") 57 | } 58 | return ids[p] 59 | } 60 | 61 | // 查看元素p和元素q是否所属一个集合 62 | // O(1)复杂度 63 | func isConnected(p:Int,q:Int) -> Bool { 64 | return find(p) == find(q) 65 | } 66 | 67 | // 合并元素p和元素q所属的集合 68 | // O(n) 复杂度 69 | func unionElements(p:Int,q:Int) { 70 | let qId = find(q) 71 | let pId = find(p) 72 | 73 | if qId == pId { 74 | return 75 | } 76 | // 合并过程需要遍历一遍所有元素, 将两个元素的所属集合编号合并 77 | for (index,item) in ids.enumerated() { 78 | if (pId == item){ 79 | ids[index] = qId 80 | } 81 | } 82 | } 83 | 84 | ``` 85 | 86 | 数组实现 87 | - 查看元素p和元素q是否所属一个集合的复杂度为O(1) 88 | - 合并元素p和元素q所属的集合的复杂度为O(n) 89 | 90 | 91 | **第一种树** 92 | 93 | ``` 94 | class UnionFind2: NSObject { 95 | // 我们的第二版Union-Find, 使用一个数组构建一棵指向父节点的树 96 | // parent[i]表示第一个元素所指向的父节点 97 | private var parent:[Int] = Array() 98 | 99 | init(size:Int) { 100 | super.init() 101 | // 初始化, 每一个parent[i]指向自己, 表示每一个元素自己自成一个集合 102 | for item in 0.. Int { 110 | if p >= parent.count || p < 0 { 111 | fatalError("p is out of bound") 112 | } 113 | // 不断去查询自己的父亲节点, 直到到达根节点 114 | // 根节点的特点: parent[p] == p 115 | var root = p 116 | while root != parent[root] { 117 | root = parent[root] 118 | } 119 | 120 | return root 121 | } 122 | 123 | // 查看元素p和元素q是否所属一个集合 124 | // O(h)复杂度, h为树的高度 125 | func isConnected(p:Int,q:Int) -> Bool { 126 | return find(p) == find(q) 127 | } 128 | 129 | // 合并元素p和元素q所属的集合 130 | // O(h)复杂度, h为树的高度 131 | func unionElements(p:Int,q:Int) { 132 | let qRoot = find(q) 133 | let pRoot = find(p) 134 | if qRoot == pRoot { 135 | return 136 | } 137 | parent[qRoot] = pRoot 138 | } 139 | 140 | } 141 | 142 | ``` 143 | 144 | 这种树不做任何判断,直接替换 145 | 146 | 147 | **第二种树** 148 | ``` 149 | class UnionFind3: NSObject { 150 | // parent[i]表示第一个元素所指向的父节点 151 | private var parent:[Int] = Array() 152 | // 表示以i为根的集合中元素个数 153 | private var sz:[Int] = Array() 154 | init(size:Int) { 155 | super.init() 156 | // 初始化, 每一个parent[i]指向自己, 表示每一个元素自己自成一个集合 157 | for item in 0.. Int { 167 | if p >= parent.count || p < 0 { 168 | fatalError("p is out of bound") 169 | } 170 | // 不断去查询自己的父亲节点, 直到到达根节点 171 | // 根节点的特点: parent[p] == p 172 | var root = p 173 | while root != parent[root] { 174 | root = parent[root] 175 | } 176 | 177 | return root 178 | } 179 | 180 | // 查看元素p和元素q是否所属一个集合 181 | // O(h)复杂度, h为树的高度 182 | func isConnected(p:Int,q:Int) -> Bool { 183 | return find(p) == find(q) 184 | } 185 | 186 | // 合并元素p和元素q所属的集合 187 | // O(h)复杂度, h为树的高度 188 | func unionElements(p:Int,q:Int) { 189 | let qRoot = find(q) 190 | let pRoot = find(p) 191 | if qRoot == pRoot { 192 | return 193 | } 194 | 195 | // 根据两个元素所在树的元素个数不同判断合并方向 196 | // 将元素个数少的集合合并到元素个数多的集合上 197 | if sz[pRoot] < sz[qRoot] { 198 | parent[pRoot] = qRoot 199 | sz[qRoot] += sz[pRoot] 200 | }else{ 201 | parent[qRoot] = pRoot; 202 | sz[pRoot] += sz[qRoot]; 203 | } 204 | 205 | } 206 | 207 | 208 | } 209 | ``` 210 | 211 | 添加了一个元素个数少的一方,添加到元素个数多的一方的判断 212 | 213 | 214 | **第三种树** 215 | 216 | ``` 217 | class UnionFind4: NSObject { 218 | // parent[i]表示第一个元素所指向的父节点 219 | private var parent:[Int] = Array() 220 | // rank[i]表示以i为根的集合所表示的树的层数 221 | private var rank:[Int] = Array() 222 | 223 | init(size:Int) { 224 | super.init() 225 | // 初始化, 每一个parent[i]指向自己, 表示每一个元素自己自成一个集合 226 | for item in 0.. Int { 235 | if p >= parent.count || p < 0 { 236 | fatalError("p is out of bound") 237 | } 238 | // 不断去查询自己的父亲节点, 直到到达根节点 239 | // 根节点的特点: parent[p] == p 240 | var root = p 241 | while root != parent[root] { 242 | root = parent[root] 243 | } 244 | 245 | return root 246 | } 247 | 248 | // 查看元素p和元素q是否所属一个集合 249 | // O(h)复杂度, h为树的高度 250 | func isConnected(p:Int,q:Int) -> Bool { 251 | return find(p) == find(q) 252 | } 253 | 254 | // 合并元素p和元素q所属的集合 255 | // O(h)复杂度, h为树的高度 256 | func unionElements(p:Int,q:Int) { 257 | let qRoot = find(q) 258 | let pRoot = find(p) 259 | if qRoot == pRoot { 260 | return 261 | } 262 | // 根据两个元素所在树的rank不同判断合并方向 263 | // 将rank低的集合合并到rank高的集合上 264 | if(rank[pRoot] < rank[qRoot]){ 265 | parent[pRoot] = qRoot; 266 | }else if(rank[qRoot] < rank[pRoot]){ 267 | parent[qRoot] = pRoot; 268 | }else{ // rank[pRoot] == rank[qRoot] 269 | parent[pRoot] = qRoot; 270 | rank[qRoot] += 1; // 此时, 我维护rank的值 271 | } 272 | 273 | } 274 | 275 | } 276 | 277 | ``` 278 | 有根据元素个数判断,变成了层数判断 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | -------------------------------------------------------------------------------- /数据结构/二叉树/二叉树/二叉树/BTS.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BTS.swift 3 | // 二叉树 4 | // 5 | // Created by yunna on 2019/4/1. 6 | // Copyright © 2019年 yunna. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | class BTS: NSObject { 12 | var size = 0 13 | var root:Node! 14 | 15 | //判断是否为空 16 | func isEmpty() -> Bool{ 17 | return size == 0; 18 | } 19 | //添加元素 20 | func add(E:Int) { 21 | if root == nil { 22 | size += 1 23 | root = Node(E: E) 24 | }else{ 25 | addNode(E: E, node: root) 26 | } 27 | } 28 | //查看二分搜索树中是否包含某个元素 29 | func contain(E:Int) -> Bool { 30 | return containNode(E: E, node: root) 31 | } 32 | 33 | //二分搜索树的前序遍历 34 | func preOrder() { 35 | preOrder(node: root) 36 | } 37 | // 二分搜索树的中序遍历 38 | func inOrder() { 39 | inOrder(node: root) 40 | } 41 | // 二分搜索树的后序遍历 42 | func postOrder() { 43 | postOrder(node: root) 44 | } 45 | // 寻找二分搜索树的最小元素 46 | func minimum() -> Int { 47 | if size == 0 { 48 | return 10086 49 | } 50 | 51 | return minimum(node: root).E 52 | } 53 | // 从二分搜索树中删除最小值所在节点, 返回最小值 54 | func removeMin() -> Int { 55 | if size == 0 { 56 | return 10086 57 | } 58 | root = removeMin(node: root) 59 | return minimum() 60 | } 61 | 62 | // 寻找二分搜索树的最大元素 63 | func maximum() -> Int { 64 | if size == 0 { 65 | return 10086 66 | } 67 | return maximum(node: root) 68 | } 69 | // 从二分搜索树中删除最大值所在节点, 返回最大值 70 | func removeMax() -> Int { 71 | if size == 0 { 72 | return 10086 73 | } 74 | root = removeMax(node: root) 75 | return maximum() 76 | } 77 | // 删除为E的节点 78 | func remove(E:Int) ->Node{ 79 | root = remove(E: E, node: root) 80 | return root 81 | } 82 | 83 | } 84 | 85 | extension BTS{ 86 | // 向以node为根的二分搜索树中插入元素e,递归算法 87 | private 88 | func addNode(E:Int,node:Node) { 89 | //递归用法,先判断结束语句 90 | if node.E == E { 91 | return 92 | }else if((E < node.E) && (node.left == nil)){ 93 | //遍历到最后,添加左叶子 94 | node.left = Node(E: E) 95 | size += 1 96 | return 97 | }else if((E > node.E) && (node.right == nil)){ 98 | //遍历到最后,添加右叶子 99 | node.right = Node(E: E) 100 | size += 1 101 | return 102 | } 103 | 104 | //递归调用 105 | if E < node.E { 106 | addNode(E: E, node: node.left ?? Node()) 107 | }else{ 108 | addNode(E: E, node: node.right ?? Node()) 109 | } 110 | 111 | } 112 | // 看以node为根的二分搜索树中是否包含元素e, 递归算法 113 | private 114 | func containNode(E:Int,node:Node?) -> Bool { 115 | if node == nil { 116 | return false 117 | } 118 | 119 | if node?.E == E{ 120 | return true 121 | }else if (node?.E)! > E { 122 | //左 123 | return containNode(E: E, node: node?.left ) 124 | }else{ 125 | //右 126 | return containNode(E: E, node: node?.right) 127 | } 128 | 129 | } 130 | 131 | // 前序遍历以node为根的二分搜索树, 递归算法 132 | private 133 | func preOrder(node:Node?) { 134 | //结束条件 135 | if node == nil { 136 | return 137 | } 138 | print(node?.E ?? "nil") 139 | preOrder(node: node?.left) 140 | preOrder(node: node?.right) 141 | } 142 | // 中序遍历以node为根的二分搜索树, 递归算法 143 | private 144 | func inOrder(node:Node?) { 145 | //结束条件 146 | if node == nil { 147 | return 148 | } 149 | inOrder(node: node?.left) 150 | print(node?.E ?? "nil") 151 | inOrder(node: node?.right) 152 | 153 | } 154 | // 后序遍历以node为根的二分搜索树, 递归算法 155 | private 156 | func postOrder(node:Node?) { 157 | //结束条件 158 | if node == nil { 159 | return 160 | } 161 | inOrder(node: node?.left) 162 | inOrder(node: node?.right) 163 | print(node?.E ?? "nil") 164 | } 165 | //查找最小数据 递归算法 166 | private 167 | func minimum(node:Node?) -> Node{ 168 | if node?.left == nil { 169 | return node ?? Node() 170 | } 171 | 172 | return minimum(node: node?.left) 173 | } 174 | 175 | // 删除掉以node为根的二分搜索树中的最小节点 176 | // 返回删除节点后新的二分搜索树的根 177 | private 178 | func removeMin(node:Node?) -> Node? { 179 | if node?.left == nil { 180 | size -= 1 181 | let rightNode = node?.right 182 | node?.right = nil 183 | return rightNode 184 | } 185 | node?.left = removeMin(node: node?.left) 186 | return node 187 | } 188 | 189 | //查找最大数据 递归算法 190 | private 191 | func maximum(node:Node?) -> Int { 192 | if node?.right == nil { 193 | return node?.E ?? 10086 194 | } 195 | return maximum(node: node?.right) 196 | } 197 | // 删除掉以node为根的二分搜索树中的最大节点 198 | // 返回删除节点后新的二分搜索树的根 199 | private 200 | func removeMax(node:Node?) -> Node? { 201 | if node?.right == nil { 202 | size -= 1 203 | let leftNode = node?.left 204 | node?.left = nil 205 | return leftNode 206 | } 207 | node?.right = removeMax(node: node?.right) 208 | return node 209 | } 210 | // 删除掉以node为根的二分搜索树中值为e的节点, 递归算法 211 | // 返回删除节点后新的二分搜索树的根 212 | private 213 | func remove(E:Int,node:Node?) -> Node? { 214 | if node == nil { 215 | return nil 216 | } 217 | if E < (node?.E)! { 218 | node?.left = remove(E: E, node: node?.left) 219 | return node 220 | }else if E > (node?.E)! { 221 | node?.right = remove(E: E, node: node?.right) 222 | return node 223 | }else{ 224 | //找到了 225 | // 待删除节点左子树为空的情况 226 | if node?.left == nil{ 227 | let rightNode = node?.right 228 | node?.right = nil 229 | size -= 1 230 | return rightNode 231 | } 232 | // 待删除节点右子树为空的情况 233 | if node?.right == nil{ 234 | let leftNode = node?.left 235 | node?.left = nil 236 | size -= 1 237 | return leftNode 238 | } 239 | // 待删除节点左右子树均不为空的情况 240 | 241 | // 找到比待删除节点大的最小节点, 即待删除节点右子树的最小节点 242 | // 用这个节点顶替待删除节点的位置 243 | let successor = minimum(node: node?.right) 244 | successor.right = removeMin(node: node?.right) 245 | successor.left = node?.left 246 | node?.right = nil 247 | node?.left = nil 248 | 249 | return successor 250 | } 251 | } 252 | } 253 | 254 | 255 | 256 | //节点 257 | class Node: NSObject { 258 | var E:Int = 0 259 | var left:Node? 260 | var right:Node? 261 | override init() { 262 | super.init() 263 | } 264 | init(E:Int) { 265 | self.E = E 266 | self.left = nil 267 | self.right = nil 268 | } 269 | 270 | } 271 | 272 | 273 | 274 | 275 | 276 | -------------------------------------------------------------------------------- /数据结构/栈和队列/栈和队列.md: -------------------------------------------------------------------------------- 1 | ## 栈和队列 2 | 3 | ### 栈 4 | 5 | 栈是一种特殊的线性表,仅能在线性表的一端操作,栈顶允许操作,栈底不允许操作。 6 | 7 | **栈的特性:后进先出(Last In First Out),简写为LIFO** 8 | 9 | ![栈](https://github.com/SunshineBrother/LeetCodeStudy/blob/master/数据结构/栈和队列/栈.png) 10 | 11 | #### 栈的实现 12 | 我们主要实现栈的一下几个方法 13 | - getSize:栈的大小 14 | - isEmpty:栈是否为空 15 | - push:压栈 16 | - pop:出栈 17 | - peek:栈顶 18 | - toString:打印 19 | 20 | ``` 21 | class Stack: NSObject { 22 | var stackArr = [Any]() 23 | 24 | //栈的大小 25 | func getSize() -> Int { 26 | return stackArr.count 27 | } 28 | //栈是否为空 29 | func isEmpty() -> Bool { 30 | return stackArr.isEmpty 31 | } 32 | //压栈 33 | func push(e:Any) { 34 | stackArr.append(e) 35 | } 36 | //出栈 37 | func pop() -> Any{ 38 | guard !stackArr.isEmpty else { 39 | return (Any).self 40 | } 41 | let E = stackArr[stackArr.count - 1] 42 | stackArr.removeLast() 43 | return E 44 | } 45 | //栈顶 46 | func peek() -> Any { 47 | return stackArr[stackArr.count - 1] 48 | } 49 | //打印 50 | func toString() ->String{ 51 | var res = "Stack: " 52 | for (index,item) in stackArr.enumerated() { 53 | if (index < stackArr.count - 1){ 54 | res.append("\(item),") 55 | } 56 | if (index == stackArr.count - 1){ 57 | res.append("\(item)") 58 | } 59 | } 60 | return res 61 | } 62 | 63 | 64 | } 65 | 66 | ``` 67 | 68 | 69 | #### 栈的使用:有效的括号 70 | 给定一个只包括 `(`,`)`,`{`,`}`,`[`,`]` 的字符串,判断字符串是否有效。 71 | 72 | 有效字符串需满足: 73 | - 1、左括号必须用相同类型的右括号闭合。 74 | - 2、左括号必须以正确的顺序闭合。 75 | 76 | 注意空字符串可被认为是有效字符串。 77 | 78 | **示例 1:** 79 | ``` 80 | 输入: "()" 81 | 输出: true 82 | ``` 83 | **示例 2:** 84 | ``` 85 | 输入: "()[]{}" 86 | 输出: true 87 | ``` 88 | **示例 3:** 89 | ``` 90 | 输入: "(]" 91 | 输出: false 92 | ``` 93 | **示例 4:** 94 | ``` 95 | 输入: "([)]" 96 | 输出: false 97 | ``` 98 | **示例 5:** 99 | ``` 100 | 输入: "{[]}" 101 | 输出: true 102 | ``` 103 | 104 | 105 | ``` 106 | class Solution: NSObject { 107 | let stack = Stack() 108 | func isValid(_ s: String) -> Bool { 109 | for c in s { 110 | if (c == "(" || c == "[" || c == "{"){ 111 | stack.push(e: c) 112 | }else{ 113 | //第一个如果不是上面的那三种情况直接return false 114 | if stack.isEmpty() { 115 | return false 116 | } 117 | //在出现第一个右括号的时候,一定有一个与之相对应的左括号,否则不符合规则;在出现第一个右括号,我们在栈中栈定元素,栈定元素应该是与右括号成对出现的,否则不符合规则 118 | let topChar = stack.pop() as! Character 119 | if (c == ")" && topChar != "("){ 120 | return false 121 | } 122 | if (c == "]" && topChar != "["){ 123 | return false 124 | } 125 | if (c == "}" && topChar != "{"){ 126 | return false 127 | } 128 | } 129 | 130 | } 131 | //在字符串遍历到最后,我们的栈应该是空的,因为左侧右侧成对出现便会被压出栈 132 | return stack.isEmpty() 133 | } 134 | } 135 | 136 | ``` 137 | 138 | **代码解析** 139 | 上面这段逻辑代码我们就使用栈思想 140 | 141 | - 1、首先第一个字符必须是`(`、`[`、`{`中的一个,否则必定不符合规则,这也就是下面这段代码 142 | ``` 143 | if (c == "(" || c == "[" || c == "{"){ 144 | stack.push(e: c) 145 | }else{ 146 | //第一个如果不是上面的那三种情况直接return false 147 | if stack.isEmpty() { 148 | return false 149 | } 150 | } 151 | ``` 152 | - 2、在出现第一个右括号的时候,一定有一个与之相对应的左括号,否则不符合规则;在出现第一个右括号,我们在栈中栈定元素,栈定元素应该是与右括号成对出现的,否则不符合规则 153 | ``` 154 | let topChar = stack.pop() as! Character 155 | if (c == ")" && topChar != "("){ 156 | return false 157 | } 158 | if (c == "]" && topChar != "["){ 159 | return false 160 | } 161 | if (c == "}" && topChar != "{"){ 162 | return false 163 | } 164 | ``` 165 | - 3、在字符串遍历到最后,我们的栈应该是空的,因为左侧右侧成对出现便会被压出栈 166 | 167 | 我们在写过代码以后可以在[leetcode](https://leetcode-cn.com/explore/)中进行验证,这一道题是[leetcode](https://leetcode-cn.com/explore/)的第20题。 168 | 169 | 170 | #### 队列 171 | 172 | - 队列,一种限定性的线性表。它只允许在表一端进行插入,而在表的另一端进行删除操作。 173 | - 队列是一种先进先出的数据结构 174 | - First In First Out(FIFO) 175 | - 只能从队尾添加元素,从队首取出元素 176 | 177 | ![队列](https://github.com/SunshineBrother/LeetCodeStudy/blob/master/数据结构/栈和队列/队列.png) 178 | 179 | 180 | **队列的简单实现** 181 | ``` 182 | class Queue: NSObject { 183 | private var queueArr = [Any]() 184 | 185 | //队列大小 186 | func getSize() -> Int { 187 | return queueArr.count 188 | } 189 | //入队 190 | func enqueue(E:Any) { 191 | queueArr.append(E) 192 | } 193 | //出队 194 | func dequeue1() -> Any { 195 | let res = queueArr[0] 196 | queueArr.remove(at: 0) 197 | return res 198 | } 199 | //打印 200 | func toString() -> String { 201 | var res = "Stack: " 202 | for (index,item) in queueArr.enumerated() { 203 | if (index < queueArr.count - 1){ 204 | res.append("\(item),") 205 | } 206 | if (index == queueArr.count - 1){ 207 | res.append("\(item)") 208 | } 209 | } 210 | 211 | return res 212 | } 213 | 214 | } 215 | ``` 216 | 217 | 对于添加队列,其实就是在队列的最后一位添加一个元素,算法复杂度为O(1) 218 | 但是删除队列,因为是删除的第一个元素其实就是走的一下这个算法,复杂度是O(n) 219 | ``` 220 | for index in 0.. Int { 241 | // 注意此时getSize的逻辑: 242 | // 如果tail >= front,非常简单,队列中的元素个数就是tail - front 243 | // 如果tail < front,说明我们的循环队列"循环"起来了,此时,队列中的元素个数为: 244 | // tail - front + data.count 245 | // 246 | // 也可以理解成,此时,data中没有元素的数目为front - tail, 247 | // 整体元素个数就是 data.count - (front - tail) = data.count + tail - front 248 | return tail >= front ? tail - front : tail - front + data.count; 249 | } 250 | //入队 251 | func enqueue(E:String) { 252 | //此时 队头 和 队尾 相同 我们需要对数组空间进行扩容,并且重新分配数组元素 253 | if (tail + 1) % size == front { 254 | //数组扩容 255 | resize() 256 | size = size * 2 257 | } 258 | //入队操作 259 | if data.count <= tail { 260 | data.append(E) 261 | }else{ 262 | data[tail] = E 263 | } 264 | 265 | //队尾计算 266 | tail = (tail + 1) % size 267 | 268 | } 269 | //出队 270 | func dequeue() -> String { 271 | let E = data[front] 272 | //把出队的数据占位 273 | data[front] = "nil" 274 | front = (front + 1) % size 275 | //数组缩容 276 | if data.count <= size / 4 277 | && data.count > 0{ 278 | resize() 279 | size = size / 2 280 | } 281 | 282 | return E 283 | } 284 | 285 | //数组调整 286 | func resize() { 287 | var newData = [String]() 288 | for (index,_) in data.enumerated() { 289 | let item = data[(index + front) % data.count] 290 | if (item == "nil"){ 291 | continue 292 | } 293 | newData.append(item) 294 | } 295 | data = newData 296 | front = 0 297 | tail = newData.count 298 | } 299 | 300 | 301 | //打印 302 | func toString() -> String { 303 | var res = "LoopQueue: " 304 | for (index,item) in data.enumerated() { 305 | if (index < data.count - 1){ 306 | res.append("\(item),") 307 | } 308 | if (index == data.count - 1){ 309 | res.append("\(item)") 310 | } 311 | } 312 | res.append(" ->tail") 313 | return res 314 | } 315 | } 316 | 317 | ``` 318 | 319 | 里面有三个注意点 320 | - 1、在出队的时候怎么处理 321 | - 2、在入队的时候怎么处理 322 | - 3、在数组重新调整的时候怎么处理 323 | - 4、在数组超级大的时候,在扩容数组的时候是否还是要全部重新调整顺序。这个时候应该是一部分一部分的调整数组的顺序,不要一次计算结束,不然太过于消耗性能 324 | 325 | #### 计算 326 | 327 | 我们说循环队列多好,还是要看实践的,我们来写两个简单的算法来计算两个时间差值,看看循环队列是否真的是节约了时间 328 | ``` 329 | import Foundation 330 | extension Date { 331 | /// 获取当前 毫秒级 时间戳 - 13位 332 | var milliStamp : Int { 333 | let timeInterval: TimeInterval = self.timeIntervalSince1970 334 | let millisecond = CLongLong(round(timeInterval*1000)) 335 | return Int(millisecond) 336 | } 337 | } 338 | 339 | //循环队列 340 | let loopQuere = LoopQueue() 341 | let startDate1 = Date().milliStamp 342 | for index in 0..<100000{ 343 | loopQuere.enqueue(E: "\(index)") 344 | if index % 2 == 1{ 345 | let _ = loopQuere.dequeue() 346 | } 347 | 348 | } 349 | let endDate1 = Date().milliStamp 350 | print("循环队列:\(endDate1 - startDate1)") 351 | 352 | 353 | //线性队列 354 | let quere = Queue() 355 | let startDate2 = Date().milliStamp 356 | for index in 0..<100000{ 357 | quere.enqueue(E: "\(index)") 358 | if index % 2 == 1{ 359 | let _ = quere.dequeue() 360 | } 361 | } 362 | let endDate2 = Date().milliStamp 363 | print("线性队列:\(endDate2 - startDate1)") 364 | ``` 365 | 366 | 在10万数量级的情况下,就相差的比较多了,如果更大的数量级,性能相差的更大 367 | ``` 368 | 循环队列:118 369 | 线性队列:4318 370 | ``` 371 | 372 | -------------------------------------------------------------------------------- /数据结构/打印树/如何直观形象地树状打印一棵二叉树?.md: -------------------------------------------------------------------------------- 1 | ## 如何直观形象地树状打印一棵二叉树? 2 | 3 | 4 | > 网上绝大部分的二叉树打印效果都十分潦草,也不够直观形象,最近自己用JS写了个图形化小工具`BinaryTreeGraph`,也用Java写了个打印器`BinaryTreePrinter`,还有个Objective-C版本`BinaryTreePrinterOC` 5 | 具体代码实现请看[github](https://github.com/CoderMJLee/BinaryTrees) 6 | 7 | 8 | ### 1、BinaryTreeGraph(JS版) 9 | 10 | * 在线演示:[BinaryTreeGraph](http://520it.com/binarytrees/) 11 | 12 | ![image.png](https://upload-images.jianshu.io/upload_images/2348494-ed058e21b121e307.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 13 | 14 | 15 | 16 | ![image.png](https://upload-images.jianshu.io/upload_images/2348494-f75d8eb17eddea6f.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 17 | 18 | 19 | 20 | ![image.png](https://upload-images.jianshu.io/upload_images/2348494-716560aca34556f9.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 21 | 22 | 23 | 24 | ![image.png](https://upload-images.jianshu.io/upload_images/2348494-629fe5dfd96e6e5e.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 25 | 26 | 27 | ![image.png](https://upload-images.jianshu.io/upload_images/2348494-ad396164cc8e4016.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 28 | 29 | 30 | 31 | ## 2、BinaryTreePrinter(Java版) 32 | 33 | ### 2.1、简介 34 | 35 | - 树状打印一棵二叉树 36 | - 比如输入一棵二叉搜索树 37 | - [381, 12, 410, 9, 40, 394, 540, 35, 190, 476, 760, 146, 445, 600, 800] 38 | 39 | - 就会输出 40 | 41 | ![image.png](https://upload-images.jianshu.io/upload_images/2348494-879f30d2134d3503.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 42 | 43 | - 或者输出 44 | 45 | ![image.png](https://upload-images.jianshu.io/upload_images/2348494-45983f535ab89e98.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 46 | 47 | ### 2.2、核心API 48 | 49 | ``` 50 | public final class BinaryTrees { 51 | // 打印一棵二叉树 52 | public static void print(BinaryTreeInfo tree); 53 | public static void print(BinaryTreeInfo tree, PrintStyle style); 54 | 55 | // 打印一棵二叉树(打印完自动换行) 56 | public static void println(BinaryTreeInfo tree); 57 | public static void println(BinaryTreeInfo tree, PrintStyle style); 58 | 59 | // 获得一棵二叉树的打印字符串 60 | public static String printString(BinaryTreeInfo tree); 61 | public static String printString(BinaryTreeInfo tree, PrintStyle style); 62 | 63 | // 可选的打印样式 64 | public enum PrintStyle { 65 | LEVEL_ORDER, 66 | INORDER 67 | } 68 | } 69 | 70 | ``` 71 | 72 | 73 | ### 2.3、示例 74 | 75 | 76 | ** 2.3.1、实现BinaryTreeInfo** 77 | 78 | - 根节点是谁? 79 | - 如何查找左节点? 80 | - 如何查找右节点? 81 | - 如何打印单个节点? 82 | 83 | 84 | ``` 85 | /** 86 | * BinarySearchTree是你自己编写的二叉树类 87 | */ 88 | public class BinarySearchTree implements BinaryTreeInfo { 89 | /**这里省略了大量代码,只贴出了脉络代码**/ 90 | 91 | private Node root; 92 | private static class Node { 93 | E element; 94 | Node left; 95 | Node right; 96 | } 97 | 98 | /********** BinaryTreeInfo **********/ 99 | @Override 100 | public Object root() { 101 | // 根节点是谁? 102 | return root; 103 | } 104 | 105 | @Override 106 | public Object left(Object node) { 107 | // 如何查找左节点? 108 | return ((Node)node).left; 109 | } 110 | 111 | @Override 112 | public Object right(Object node) { 113 | // 如何查找右节点? 114 | return ((Node)node).right; 115 | } 116 | 117 | @Override 118 | public Object string(Object node) { 119 | // 如何打印单个节点? 120 | return ((Node)node).element; 121 | } 122 | /********** BinaryTreeInfo **********/ 123 | } 124 | 125 | ``` 126 | 127 | 128 | **2.3.2、打印** 129 | 130 | ``` 131 | // 随机生成的一棵二叉搜索树(random generation) 132 | BinarySearchTree bst = ...; 133 | 134 | // PrintStyle.LEVEL_ORDER(层序打印) 135 | BinaryTrees.println(bst); 136 | 137 | // PrintStyle.INORDER(中序打印) 138 | BinaryTrees.println(bst, PrintStyle.INORDER); 139 | 140 | ``` 141 | 142 | ![image.png](https://upload-images.jianshu.io/upload_images/2348494-1195b4308cd9cfec.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 143 | 144 | ![image.png](https://upload-images.jianshu.io/upload_images/2348494-5da199c529f14fd2.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 145 | 146 | 147 | 148 | **2.3.3、生成字符串写入文件** 149 | 150 | ``` 151 | Files.writeToFile("F:/test/bst.txt", BinaryTrees.printString(bst)); 152 | ``` 153 | 154 | **2.3.4、不需要定义二叉树类** 155 | 156 | ``` 157 | BinaryTrees.println(new BinaryTreeInfo() { 158 | @Override 159 | public Object root() { 160 | return 8; 161 | } 162 | 163 | @Override 164 | public Object left(Object node) { 165 | if (node.equals(8)) return 3; 166 | if (node.equals(3)) return 1; 167 | if (node.equals(6)) return 4; 168 | if (node.equals(14)) return 13; 169 | return null; 170 | } 171 | 172 | @Override 173 | public Object right(Object node) { 174 | if (node.equals(8)) return 10; 175 | if (node.equals(10)) return 14; 176 | if (node.equals(3)) return 6; 177 | if (node.equals(6)) return 7; 178 | return null; 179 | } 180 | 181 | @Override 182 | public Object string(Object node) { 183 | return node; 184 | } 185 | }); 186 | 187 | BinaryTrees.println(new BinaryTreeInfo() { 188 | @Override 189 | public Object root() { 190 | return "Life"; 191 | } 192 | 193 | @Override 194 | public Object left(Object node) { 195 | if (node.equals("Life")) return "Animal"; 196 | if (node.equals("Person")) return "Man"; 197 | if (node.equals("Animal")) return "Cat"; 198 | if (node.equals("Dog")) return "Teddy"; 199 | return null; 200 | } 201 | 202 | @Override 203 | public Object right(Object node) { 204 | if (node.equals("Life")) return "Person"; 205 | if (node.equals("Person")) return "Woman"; 206 | if (node.equals("Animal")) return "Dog"; 207 | if (node.equals("Dog")) return "SingleDog"; 208 | return null; 209 | } 210 | 211 | @Override 212 | public Object string(Object node) { 213 | return node; 214 | } 215 | }); 216 | 217 | ``` 218 | 219 | ![image.png](https://upload-images.jianshu.io/upload_images/2348494-a1272f3164e2721d.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 220 | 221 | 222 | 223 | ![image.png](https://upload-images.jianshu.io/upload_images/2348494-a14eac9fa8f25ccf.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 224 | 225 | 226 | 227 | 228 | **2.3.5、二叉堆** 229 | 230 | ``` 231 | public class BinaryHeap implements BinaryTreeInfo { 232 | private int size; 233 | private E[] elements; 234 | 235 | @Override 236 | public Object root() { 237 | return 0; 238 | } 239 | 240 | @Override 241 | public Object left(Object node) { 242 | int leftIndex = ((int)node << 1) + 1; 243 | if (leftIndex >= size) return null; 244 | return leftIndex; 245 | } 246 | 247 | @Override 248 | public Object right(Object node) { 249 | int rightIndex = ((int)node << 1) + 2; 250 | if (rightIndex >= size) return null; 251 | return rightIndex; 252 | } 253 | 254 | @Override 255 | public Object string(Object node) { 256 | return elements[(int) node]; 257 | } 258 | } 259 | 260 | BinaryHeap heap = new BinaryHeap<>(); 261 | for (int i = 0; i < 10; i++) { 262 | heap.add((int)(Math.random() * 100)); 263 | } 264 | BinaryTrees.println(heap); 265 | 266 | ``` 267 | 268 | 269 | ![image.png](https://upload-images.jianshu.io/upload_images/2348494-58db087e05378548.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 270 | 271 | 272 | ## 3、BinaryTreePrinterOC 273 | 274 | 实现MJBinaryTreeInfo协议 275 | 276 | ``` 277 | @interface MJBSTNode : NSObject { 278 | @public 279 | id _element; 280 | MJBSTNode *_left; 281 | MJBSTNode *_right; 282 | } 283 | @end 284 | 285 | @interface MJBinarySearchTree : NSObject 286 | @end 287 | 288 | @interface MJBinarySearchTree() { 289 | MJBSTNode *_root; 290 | } 291 | @end 292 | 293 | @implementation MJBinarySearchTree 294 | #pragma mark - MJBinaryTreeInfo 295 | - (id)left:(MJBSTNode *)node { 296 | return node->_left; 297 | } 298 | 299 | - (id)right:(MJBSTNode *)node { 300 | return node->_right; 301 | } 302 | 303 | - (id)string:(MJBSTNode *)node { 304 | return node->_element; 305 | } 306 | 307 | - (id)root { 308 | return _root; 309 | } 310 | @end 311 | 312 | ``` 313 | 314 | 打印 315 | 316 | ``` 317 | [MJBinaryTrees println:bst]; 318 | 319 | [MJBinaryTrees println:bst style:MJPrintStyleLevelOrder]; 320 | 321 | [MJBinaryTrees println:bst style:MJPrintStyleInorder]; 322 | 323 | NSString *str = [MJBinaryTrees printString:bst]; 324 | NSString *file = @"/Users/mj/Desktop/1.txt"; 325 | [str writeToFile:file atomically:YES encoding:NSUTF8StringEncoding error:nil]; 326 | 327 | ``` 328 | 329 | 330 | 文章转载自: [如何直观形象地树状打印一棵二叉树?](https://www.cnblogs.com/mjios/p/10627814.html) 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | 342 | 343 | 344 | 345 | 346 | 347 | 348 | 349 | --------------------------------------------------------------------------------