动态数组
装箱 :装箱是指将值类型转换为引用类型的过程。值类型(如 int、char、struct 等)通常存储在栈上,而引用类型存储在堆上。当进行装箱操作时,会在堆上为值类型创建一个对象实例,并将值类型的值复制到该对象中,最后返回这个对象的引用。
拆箱 :拆箱则是将引用类型转换为值类型的过程。它需要先检查引用类型是否为某个特定值类型的装箱实例,然后将堆上对象中存储的值复制到栈上的新值类型变量中。
装箱开销 :装箱操作会在堆上分配内存,并且需要复制值类型的值,这会带来一定的性能开销,尤其是在频繁进行装箱操作时,会导致内存分配和垃圾回收的压力增加。
拆箱开销 :拆箱操作需要进行类型检查,确保引用类型确实是某个值类型的装箱实例,这也会带来一定的性能开销。
动态数组指的是是大小能在程序运行期间动态调整的数组,可根据实际需求增添或删减元素。与固定大小的数组不同,动态数组能够灵活应对元素数量的变化,从而更高效地管理内存。以下通过自定义的类实现动态数组,使用泛型类,来进行动态数组的实现,这样可以根据数组的类型,实现相应的功能,同时实现IEnumerable接口,可以被foreach循环遍历。提高更高效的数组实现功能
1 2 3 4 5 6 7 8 9 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 35 36 37 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 63 64 65 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 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 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 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 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 using System;using System.Collections;using System.Collections.Generic;using System.Text;namespace CSharp_project { class Array1 <T > : IEnumerable <T > { private T[] data; private int N; public Array1 () { data = new T[10 ]; N = 0 ; } public Array1 (int capacity ) { data = new T[capacity]; N = 0 ; } public int Capacity { get { return data.Length; } } public int Count { get { return N; } } private void CheckIndex (int index ) { if (index < 0 || index >= N) { throw new ArgumentOutOfRangeException("数组索引越界" ); } } private void Resize (int newCapacity ) { T[] newData = new T[newCapacity]; Array.Copy(data, newData, N); data = newData; } public void Add (int index, T e ) { if (index < 0 || index > N) { throw new ArgumentOutOfRangeException("数组索引越界" ); } if (N == data.Length) { Resize(2 * data.Length); } if (e == null && typeof (T).IsClass) { throw new ArgumentNullException(nameof (e)); } for (int i = N - 1 ; i >= index; i--) { data[i + 1 ] = data[i]; } data[index] = e; N++; } public void AddLast (T e ) { Add(N, e); } public void AddHead (T e ) { Add(0 , e); } public T Get (int index ) { CheckIndex(index); return data[index]; } public T GetHead () { return Get(0 ); } public T GetLast () { return Get(N - 1 ); } public bool IsContains (T e ) { for (int i = 0 ; i < N; i++) { if (EqualityComparer<T>.Default.Equals(data[i], e)) return true ; } return false ; } public int IndexOf (T e ) { for (int i = 0 ; i < N; i++) { if (EqualityComparer<T>.Default.Equals(data[i], e)) return i; } return -1 ; } public void DeleteAt (int index ) { CheckIndex(index); for (int i = index; i < N - 1 ; i++) { data[i] = data[i + 1 ]; } N--; data[N] = default (T); if (N == data.Length / 4 && data.Length / 2 > 0 ) { Resize(data.Length / 2 ); } } public void DeleteHead () { DeleteAt(0 ); } public void DeleteLast () { DeleteAt(N - 1 ); } public void DeleteElement (T e ) { int index = IndexOf(e); if (index != -1 ) { DeleteAt(index); } } public void Set (int index, T newvalue ) { CheckIndex(index); if (newvalue == null && typeof (T).IsClass) { throw new ArgumentNullException(nameof (newvalue)); } data[index] = newvalue; } public void Clear () { for (int i = 0 ; i < N; i++) { data[i] = default (T); } N = 0 ; } public void InsertRange (int index, IEnumerable<T> items ) { if (index < 0 || index > N) { throw new ArgumentOutOfRangeException("数组索引越界" ); } if (items == null ) { throw new ArgumentNullException(nameof (items)); } int countToAdd = 0 ; foreach (var item in items) { countToAdd++; } if (N + countToAdd > data.Length) { Resize(Math.Max(data.Length * 2 , N + countToAdd)); } for (int i = N - 1 ; i >= index; i--) { data[i + countToAdd] = data[i]; } int j = 0 ; foreach (var item in items) { data[index + j] = item; j++; } N += countToAdd; } public IEnumerator<T> GetEnumerator () { for (int i = 0 ; i < N; i++) { yield return data[i]; } } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } public override string ToString () { StringBuilder res = new StringBuilder(); res.Append(string .Format("Array1: count ={0} capacity{1} \n" , N, data.Length)); res.Append("[" ); for (int i = 0 ; i < N; i++) { res.Append(data[i]); if (i != N - 1 ) { res.Append(", " ); } } res.Append("]" ); return res.ToString(); } } }
链表 假设我们有一个简单的链表,存储的是整数类型的数据,当前链表包含三个节点,分别存储 1、2、3,初始状态下 head 指向存储 1 的节点。现在要插入一个新节点,其数据为 0,执行 head = new Node(0, head); 后的变化如下:
初始状态
1 head ---> [1] ---> [2] ---> [3] ---> null
执行 new Node(0, head)*
首先创建一个新节点,数据为 0,其 next 指针指向原来的头节点(存储 1 的节点)。此时新节点的状态为:
1 [0] ---> [1] ---> [2] ---> [3] ---> null
执行 head = new Node(0, head)
把 head 指向新创建的节点,更新后的链表状态为:
1 head ---> [0] ---> [1] ---> [2] ---> [3] ---> null
1 2 3 4 5 6 7 8 9 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 35 36 37 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 63 64 65 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 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 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 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading.Tasks;namespace CSharp_project { class LinkList1 <T > { private class Node { public T e; public Node next; public Node (T e, Node next ) { this .e = e; this .next = next; } public Node (T e ) { this .e = e; this .next = null ; } public override string ToString () { return e.ToString(); } } private Node head; private int N; public LinkList1 () { head = null ; N = 0 ; } public int Count { get { return N; } } public bool IsEmpty { get { return N == 0 ; } } private void CheckIndex (int index ) { if (index < 0 || index > N) { throw new ArgumentOutOfRangeException($"索引越界,当前索引: {index} ,链表节点数: {N} " ); } } public void Add (int index, T e ) { CheckIndex(index); if (index == 0 ) { Node node = new Node(e); node.next = head; head = node; } else { Node pre = head; for (int i = 0 ; i < index - 1 ; i++) pre = pre.next; Node node = new Node(e); node.next = pre.next; pre.next = node; } N++; } public void AddHead (T e ) { Add(0 , e); } public void AddLast (T e ) { Add(N, e); } public T Get (int index ) { CheckIndex(index); Node n = head; for (int i = 0 ; i < index; i++) { n = n.next; } return n.e; } public T GetHead () { return Get(0 ); } public T GetLast () { return Get(N - 1 ); } public void Set (int index, T newe ) { CheckIndex(index); Node n = head; for (int i = 0 ; i < index; i++) n = n.next; n.e = newe; } public bool IsContains (T e ) { Node n = head; for (int i = 0 ; i < N; i++) { if (n.e.Equals(e)) return true ; n = n.next; } return false ; } public void DeleteAt (int index ) { CheckIndex(index); if (index == 0 ) { head = head.next; } else { Node pre = head; for (int i = 0 ; i < index - 1 ; i++) { pre = pre.next; } pre.next = pre.next.next; } N--; } public void DeleteHead () { DeleteAt(0 ); } public void DeleteLast () { DeleteAt(N - 1 ); } public override string ToString () { StringBuilder res = new StringBuilder(); Node cur = head; while (cur != null ) { res.Append(cur + "->" ); cur = cur.next; } res.Append("Null" ); return res.ToString(); } } }
反转链表 头指针(Head Pointer) :
它是一个指针变量(如 ListNode* head或 ListNode head),存储的是链表的第一个节点(头节点)的地址(引用)。
如果链表为空,head应该是 null。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public static Node reverseLinkedList (Node head ) { Node pre = null ; Node next = null ; while (head != null ) { next = head.next ; head.next = pre; pre = head; head= next ; } return pre; }
把给定的值都删除 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 public static Node DeleteNum (Node head, int num ){ while (head != null ) { if (head.e != num) { break ; } head = head.next ; } Node cur = head; Node pre = head; while (cur != null ) { if (cur.e == num) { pre.next = cur.next ; } else { pre = cur ; } cur = cur. next ; } return head; }
双向循环链表 双向循环链表是基于单线的链表的前提的下,对每个节点都添加一个指向前一个节点的指针,包括头节点和尾节点,在逻辑上构成首位相连。对于每一个节点都有一个指向后一个节点和指向前一个节点的指针,以及存储的元素。
1 2 3 4 5 6 7 8 9 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 35 36 37 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 63 64 65 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 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 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 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 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 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 using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading.Tasks;namespace CSharp_project { class DoublyLinkedList1 <T > { class Node { public T e; public Node next; public Node pre; public Node (T e ) { this .e = e; this .next = null ; this .pre = null ; } public Node (T e,Node next,Node pre ) { this .next = next; this .pre = pre; this .e = e; } } private Node prev; private int N; private Node end; public DoublyLinkedList1 () { prev = null ; end = null ; N = 0 ; } public int Count { get { return N; } } public bool IsEmpty { get { return N == 0 ; } } private void CheckIndex (int index ) { if (index < 0 || index > N) { throw new ArgumentOutOfRangeException($"索引越界,当前索引: {index} ,链表节点数: {N} " ); } } public void Add (int index,T e ) { CheckIndex(index); Node newnode = new Node(e); if (IsEmpty) { prev = newnode; end = newnode; } else if (index == 0 ) { newnode.next = prev; prev.pre = newnode; prev = newnode; } else if (index == N) { newnode.pre = end; end.next = newnode; end = newnode; } else { Node node = prev; for (int i = 0 ; i < index; i++) node = node.next; newnode.next = node; newnode.pre = node.pre; node.pre.next = newnode; node.pre = newnode; } } public void AddHead (T e ) { Add(0 , e); } public void AddLast (T e ) { Add(N, e); } public T Get (int index ) { CheckIndex(index); Node n = prev; for (int i = 0 ; i < index; i++) { n = n.next; } return n.e; } public T GetHead () { if (IsEmpty) { throw new InvalidOperationException("链表为空,无法获取头节点。" ); } return Get(0 ); } public T GetLast () { if (IsEmpty) { throw new InvalidOperationException("链表为空,无法获取尾节点。" ); } return Get(N - 1 ); } public void Set (int index, T newe ) { CheckIndex(index); Node n = prev; for (int i = 0 ; i < index; i++) n = n.next; n.e = newe; } public bool IsContains (T e ) { Node n = prev; for (int i = 0 ; i < N; i++) { if (n.e.Equals(e)) return true ; n = n.next; } return false ; } public void DeleteAt (int index ) { CheckIndex(index); if (IsEmpty) { return ; } if (index == 0 ) { if (prev.next != null ) { prev.next.pre = null ; } else { end = null ; } prev = prev.next; } else if (index == N - 1 ) { if (end.pre != null ) { end.pre.next = null ; } else { prev = null ; } end = end.pre; } else { Node node = prev; for (int i = 0 ; i < index; i++) { node = node.next; } node.pre.next = node.next; node.next.pre = node.pre; } N--; } public void DeleteHead () { DeleteAt(0 ); } public void DeleteLast () { DeleteAt(N - 1 ); } public void DeleteElement (T e ) { Node current = prev; while (current != null ) { if (current.e.Equals(e)) { if (current.pre != null ) { current.pre.next = current.next; } else { prev = current.next; } if (current.next != null ) { current.next.pre = current.pre; } else { end = current.pre; } N--; current = current.next; } else { current = current.next; } } } public override string ToString () { StringBuilder res = new StringBuilder(); if (IsEmpty) { res.Append("Null" ); } else { Node cur = prev; res.Append("Null <-> " ); while (cur != null ) { res.Append(cur.e); if (cur.next != null ) { res.Append(" <-> " ); } cur = cur.next; } res.Append(" <-> Null" ); } return res.ToString(); } } }
双向反转链表 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 public static void reverseLinkedList (DNode head ) { DNode cur = head; DNode temp = null ; while (cur!=null ){ temp = cur.prev; cur.prev = cur.next; cur.next = temp; cur = cur.prev } if (temp!=null ) head = temp.prev; return head; }
队列和栈 基础的队列结构 1 2 3 4 5 6 7 8 9 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 35 36 37 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 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 public class Queue <T >{ private T[] elements; private int front; private int rear; private int capacity; private int count; public Queue (int size = 10 ) { capacity = size; elements = new T[capacity]; front = 0 ; rear = -1 ; count = 0 ; } public void Enqueue (T item ) { if (count == capacity) { Resize(capacity * 2 ); } rear = (rear + 1 ) % capacity; elements[rear] = item; count++; } public T Dequeue () { if (IsEmpty()) { throw new InvalidOperationException("Queue is empty" ); } T item = elements[front]; front = (front + 1 ) % capacity; count--; if (count > 0 && count == capacity / 4 ) { Resize(capacity / 2 ); } return item; } public T Peek () { if (IsEmpty()) { throw new InvalidOperationException("Queue is empty" ); } return elements[front]; } public bool IsEmpty () { return count == 0 ; } public int Count { get { return count; } } private void Resize (int newCapacity ) { T[] newArray = new T[newCapacity]; for (int i = 0 ; i < count; i++) { newArray[i] = elements[(front + i) % capacity]; } elements = newArray; front = 0 ; rear = count - 1 ; capacity = newCapacity; } }
基础的栈结构 1 2 3 4 5 6 7 8 9 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 35 36 37 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 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 using System;public class Stack <T >{ private T[] elements; private int top; private int capacity; public Stack (int size = 10 ) { capacity = size; elements = new T[capacity]; top = -1 ; } public void Push (T item ) { if (top == capacity - 1 ) { Resize(capacity * 2 ); } elements[++top] = item; } public T Pop () { if (IsEmpty()) { throw new InvalidOperationException("Stack is empty" ); } T item = elements[top--]; if (top > 0 && top == capacity / 4 ) { Resize(capacity / 2 ); } return item; } public T Peek () { if (IsEmpty()) { throw new InvalidOperationException("Stack is empty" ); } return elements[top]; } public bool IsEmpty () { return top == -1 ; } public int Count { get { return top + 1 ; } } private void Resize (int newCapacity ) { T[] newArray = new T[newCapacity]; for (int i = 0 ; i <= top; i++) { newArray[i] = elements[i]; } elements = newArray; capacity = newCapacity; } }
用双向链表实现栈和队列 栈
双向链表实现栈(Stack)
特点 :后进先出(LIFO)
核心操作 :
Push(入栈) :在链表头部插入新节点,并更新栈顶指针。
Pop(出栈) :移除链表头部节点,并返回其值,更新栈顶指针。
Peek(查看栈顶) :返回头部节点的值,不移除。
关键点 :
只需维护一个 head指针(栈顶)。
入栈和出栈都在头部操作,保证 O(1) 时间复杂度。
1 2 3 4 5 6 7 8 9 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 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 public class DoubleLinkedStack { private class DNode { public int data; public DNode prev; public DNode next; public DNode (int data ) { this .data = data; } } private DNode head; private int size; public void Push (int value ) { DNode newNode = new DNode(value ); if (head == null ) { head = newNode; } else { newNode.next = head; head.prev = newNode; head = newNode; } size++; } public int Pop () { if (head == null ) { throw new Exception("Stack is Empty" ); } int value = head.data; head = head.next; if (head != null ) { head.prev = null ; } size--; return value ; } public int Peek () { if (head == null ) { throw new Exception("Stack is empty" ); } return head.data; } public bool IsEmpty () { return head == null ; } public int Size () { return size; } }
队列
双向链表实现队列(Queue)
特点 :先进先出(FIFO)
核心操作 :
EnQueue(入队) :在链表尾部插入新节点,更新尾指针。
DeQueue(出队) :移除链表头部节点,并返回其值,更新头指针。
Peek(查看队头) :返回头部节点的值,不移除。
关键点 :
需要维护 head(队头)和 tail(队尾)两个指针。
入队在尾部,出队在头部,保证 O(1) 时间复杂度。
1 2 3 4 5 6 7 8 9 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 35 36 37 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 63 64 65 public class DoubleLinkedQueue { private class DNode { public int data; public DNode prev; public DNode next; public DNode (int data ) { this .data = data; } } private DNode front; private DNode rear; private int size; public void EnQueue (int value ) { DNode newNode = new DNode(value ); if (rear == null ) { front = rear = newNode; } else { rear.next = newNode; newNode.prev = rear; rear = newNode; } size++; } public int DeQueue () { if (front == null ) { throw new Exception("Queue is Empty" ); } int value = front.data; front = front.next; if (front == null ) { rear = null ; } else { front.prev = null ; } size--; return value ; } public int Peek () { if (front == null ) { throw new Exception("Queue is empty" ); } return front.data; } public bool IsEmpty () { return front == null ; } public int Size () { return size; } }
双端队列
双向链表实现双端队列(Deque)
特点 :两端均可插入和删除
核心操作 :
AddFront(头部插入) :在链表头部插入新节点,更新头指针。
AddRear(尾部插入) :在链表尾部插入新节点,更新尾指针。
RemoveFront(头部删除) :移除头部节点并返回其值,更新头指针。
RemoveRear(尾部删除) :移除尾部节点并返回其值,更新尾指针。
PeekFront/PeekRear :查看头部或尾部节点的值。
关键点 :
需要维护 head和 tail指针。
头部和尾部操作均需保证 O(1) 时间复杂度。
1 2 3 4 5 6 7 8 9 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 35 36 37 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 63 64 65 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 public class DoubleLinkedDeque { private class DNode { public int data; public DNode prev; public DNode next; public DNode (int data ) { this .data = data; } } private DNode front; private DNode rear; private int size; public void EnQueueRear (int value ) { DNode newNode = new DNode(value ); if (rear == null ) { front = rear = newNode; } else { rear.next = newNode; newNode.prev = rear; rear = newNode; } size++; } public void EnQueueFront (int value ) { DNode newNode = new DNode(value ); if (front == null ) { front = rear = newNode; } else { newNode.next = front; front.prev = newNode; front = newNode; } size++; } public int DeQueueRear () { if (rear == null ) { throw new Exception("Queue is Empty" ); } int value = rear.data; rear = rear.prev; if (rear == null ) { front = null ; } else { rear.next = null ; } size--; return value ; } public int DeQueueFront () { if (front == null ) { throw new Exception("Queue is Empty" ); } int value = front.data; front = front.next; if (front == null ) { rear = null ; } else { front.prev = null ; } size--; return value ; } public int PeekFront () { if (front == null ) throw new Exception("Queue is empty" ); return front.data; } public int PeekRear () { if (rear == null ) throw new Exception("Queue is empty" ); return rear.data; } public bool IsEmpty () { return front == null ; } public int Size () { return size; } }
用数组实现栈和队列 队列
pushi(入队指针) :
指向下一个可插入的位置 。
插入元素后,pushi移动到下一个位置(如果到达数组末尾则回到 0)。
popi(出队指针) :
指向当前队头元素 。
出队后,popi移动到下一个位置(如果到达数组末尾则回到 0)。
size :
记录当前队列中的元素数量,避免 pushi和 popi重叠时无法区分队列是空 还是满 。
循环利用数组空间 :
当指针到达数组末尾时,通过 NextIndex回到 0,实现循环队列 的特性。
1 2 3 4 5 6 7 8 9 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 35 36 37 38 39 40 41 42 43 44 45 46 public class MyQueue { private int [] arr; private int pushi; private int popi; private int size; private int limit; public MyQueue (int limit ) { arr = new int [limit]; pushi = 0 ; popi = 0 ; size = 0 ; this .limit = limit; } public void Push (int value ) { if (size == limit) { throw new Exception("队列已满" ); } arr[pushi] = value ; pushi = NextIndex(pushi); size++; } public int Pop () { if (size == 0 ) { throw new Exception("队列为空,无法弹出元素" ); } int value = arr[popi]; popi = NextIndex(popi); size--; return value ; } public bool IsEmpty () { return size == 0 ; } private int NextIndex (int index ) { return index < limit - 1 ? index + 1 : 0 ; } }
栈 1 2 3 4 5 6 7 8 9 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 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 public class Stack { private int [] arr; private int top; private int size; private int limit; public Stack (int limit ) { arr = new int [limit]; top = -1 ; size = 0 ; this .limit = limit; } public void Push (int value ) { if (size == limit) { throw new Exception("栈已满" ); } top++; arr[top] = value ; size++; } public int Pop () { if (size == 0 ) { throw new Exception("栈为空,无法弹出元素" ); } int value = arr[top]; top--; size--; return value ; } public int Peek () { if (size == 0 ) { throw new Exception("栈为空" ); } return arr[top]; } public bool IsEmpty () { return size == 0 ; } public int Size () { return size; } }
面试题 实现一个特殊的栈,在基本功能的基础上,在实现返回栈中的最小元素的功能。
pop,push,getMin操作时的时间复杂度都是O(1).
设计的栈类型可以使用现成的栈结构
思路:
主栈 :存储所有元素,支持常规的push和pop操作
辅助栈(最小栈) :栈顶始终存储当前主栈中的最小元素
每当主栈push一个新元素时,比较新元素与最小栈栈顶元素:
如果新元素更小(或等于),则同时将其push到最小栈
否则,重复push最小栈当前的栈顶元素
这样,最小栈的栈顶始终是主栈当前的最小值,且所有操作都保持O(1)时间复杂度。
实现思路:
1 2 3 4 5 6 7 8 9 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 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 public class MinStack { private Stack<int > stack; private Stack<int > minStack; public MinStack () { stack = new Stack<int >(); minStack = new Stack<int >(); } public void Push (int value ) { stack.Push(value ); if (minStack.IsEmpty() || value <= minStack.Peek()) { minStack.Push(value ); } else { minStack.Push(minStack.Peek()); } } public void Pop () { if (stack.IsEmpty()) { throw new InvalidOperationException("Stack is empty" ); } stack.Pop(); minStack.Pop(); } public int Top () { if (stack.IsEmpty()) { throw new InvalidOperationException("Stack is empty" ); } return stack.Peek(); } public int GetMin () { if (minStack.IsEmpty()) { throw new InvalidOperationException("Stack is empty" ); } return minStack.Peek(); } }
如何用栈结构实现队列
入队(AddElement) :
直接将元素压入PushStack
然后尝试将PushStack元素转移到PopStack(保持队列顺序)
出队(PopTo) :
如果PopStack为空,将PushStack所有元素弹出并压入PopStack(这会反转元素顺序)
从PopStack弹出栈顶元素(即队列头部)
查看队首(Peek) :
1 2 3 4 5 6 7 8 9 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 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 using System;using System.Collections.Generic;public class TwoStackToQueue { private Stack<int > popStack; private Stack<int > pushStack; public TwoStackToQueue () { popStack = new Stack<int >(); pushStack = new Stack<int >(); } private void PushToPop () { if (popStack.Count == 0 ) { while (pushStack.Count > 0 ) { popStack.Push(pushStack.Pop()); } } } public void AddElement (int e ) { pushStack.Push(e); PushToPop(); } public int PopTo () { if (popStack.Count == 0 && pushStack.Count == 0 ) { throw new InvalidOperationException("Queue is empty" ); } PushToPop(); return popStack.Pop(); } public int Peek () { if (popStack.Count == 0 && pushStack.Count == 0 ) { throw new InvalidOperationException("Queue is empty" ); } PushToPop(); return popStack.Peek(); } }
或者是
1 2 3 4 5 6 7 8 9 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 35 36 public class MyQueue { Stack<int > PopStack; Stack<int > PushStack; public MyQueue () { PopStack = new Stack<int >(); PushStack = new Stack<int >(); } public void Push (int x ) { PushStack.Push(x); } public int Pop () { if (PopStack.Count == 0 ){ while (PushStack.Count > 0 ){ PopStack.Push(PushStack.Pop()); } } return PopStack.Pop(); } public int Peek () { if (PopStack.Count == 0 ){ while (PushStack.Count > 0 ){ PopStack.Push(PushStack.Pop()); } } return PopStack.Peek(); } public bool Empty () { return PushStack.Count == 0 && PopStack.Count == 0 ; } }
如何用队列结构实现栈 操作逻辑 :
入栈(Enqueue) :
直接加入主队列(MainQueue)
时间复杂度:O(1)
出栈(DeQueue) :
将主队列中除最后一个元素外的所有元素转移到临时队列(TempQueue)
弹出并返回主队列最后一个元素(即栈顶)
交换两个队列的角色(保证下次操作仍从主队列开始)
时间复杂度:O(n) (需转移n-1个元素)
查看栈顶(Peek) :
同出栈操作,但不移除最后元素
时间复杂度:O(n)
1 2 3 4 5 6 7 8 9 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 35 36 37 38 39 40 41 42 public class MyStack { Queue<int > MainQueue; Queue<int > TempQueue; public MyStack () { MainQueue = new Queue<int >(); TempQueue = new Queue<int >(); } public void Push (int x ) { MainQueue.Enqueue(x); } public int Pop () { while (MainQueue.Count > 1 ){ TempQueue.Enqueue(MainQueue.Dequeue()); } var item = MainQueue.Dequeue(); var tmp = MainQueue; MainQueue = TempQueue; TempQueue = tmp; return item; } public int Top () { while (MainQueue.Count > 1 ){ TempQueue.Enqueue(MainQueue.Dequeue()); } var item = MainQueue.Peek(); TempQueue.Enqueue(MainQueue.Dequeue()); var tmp = MainQueue; MainQueue = TempQueue; TempQueue = tmp; return item; } public bool Empty () { return MainQueue.Count == 0 ;; } }
递归 master公式:分析递归的时间复杂度