赞
踩
大家好,这是自己整理的C#常见数据结构笔记,方便自己学习的同时分享出来,感谢支持。
声明:因为C#作为一门面向对象的编程语言, 其实大多数的像array,linklist,queue,stack等的数据结构微软的开发团队已经帮我们封装好了,所以在这里直接用这些东西显得没什么意义,所以本章从其它角度出发,用泛型实现数组,用数组实现栈,队列等这样以此类推, 欢迎学习交流!
线性结构是有序的数据元素的集合,存在着一对一的关系。常用的线性结构有:线性表,栈,队列,双队列,串(一维数组)。
一组具有同一属性的数据就称为数组(array),数组是有序数据的集合,其中各数据的排列时有一定的规律的,我们用一个数组名和下标来唯一的确定数组中的元素。
// 简单实现数组,数组涉及的应用方法很多 // 这里只实现增删功能, 其它方法逻辑差不多 public class MyArray<T> { private T[] myArray; private int arraysize; public int Count { get { return arraysize; } } // 属性或索引器必须至少有一个访问器 public T This[int index] { get { return myArray[index]; } set { myArray[index] = value; } } public MyArray() { arraysize = 0; // 初始化数组大小 myArray = new T[arraysize]; } public T GetValue(int index) { return myArray[index]; } // 向数组添加新元素 public void Add(T data) { if (data == null) // 元素为空的清空 { return; } T[] array = new T[arraysize + 1]; //申请一块新的内存 for (int i = 0; i < myArray.Length; i++) { array[i] = myArray[i]; // 先把之前的元素添加进来 } array[arraysize] = data; myArray = null; myArray = array; arraysize++; array = null; // 释放原来的array内存 } // 按照索引删除数据 public void RemoveAt(int index) { if (index < 0 && index > arraysize) { return; } for (int i = index; i < arraysize - 1; i++) { myArray[i] = myArray[i + 1]; } myArray[arraysize - 1] = default(T); arraysize--; } // 指定元素删除 public void Remove(T n) { for (int i = 0; i < myArray.Length; i++) { if (myArray[i].Equals(n)) { RemoveAt(i); } } } }
链表是一种数据元素按照链式存储结构进行存储的数据结构,在每一个节点里存到下一个节点的地址,这种存储结构具有在物理上存在非连续的特点, 链表分为单向链表,双向链表以及循环链表。
单线链表:
public interface ILinkList { bool IsEmpty(); void Unshift(Object obj); Object Shift(); void Push(Object obj); Object Pop(); bool Contain(Object obj); void Delete(Object obj); void PrintAll(); Object GetHead(); Object GetTail(); void Clear(); } class LinkListNode { public Object value; public LinkListNode next; public LinkListNode(Object value, LinkListNode next) { this.value = value; this.next = next; } public LinkListNode(Object value) { this.value = value; this.next = null; } } // 实现单向链表为例子 public class OneWayList : ILinkList { private LinkListNode head, tail; public OneWayList() { this.head = this.tail = null; } // 判断是否为空 public bool IsEmpty() { return head == null; } public void Unshift(Object obj) { head = new LinkListNode(obj, head); if (tail == null) tail = head; } public Object Shift() { if (head == null) throw new NullReferenceException(); Object value = head.value; if (head == tail) head = tail = null; else head = head.next; return value; } public void Push(Object obj) { if (!IsEmpty()) { tail.next = new LinkListNode(obj); tail = tail.next; } else head = tail = new LinkListNode(obj); } public Object Pop() { if (head == null) throw new NullReferenceException(); Object obj = tail.value; if (head == tail) head = tail = null; else { // 查找前驱节点 for (LinkListNode temp = head; temp.next != null && !temp.next.Equals(tail); temp = temp.next) tail = temp; tail.next = null; } return obj; } public void PrintAll() { string result = ""; for (LinkListNode temp = head; temp != null; temp = temp.next) result += " " + temp.value.ToString(); Console.WriteLine(result); } public bool Contain(Object obj) { if (head == null) return false; else { for (LinkListNode temp = head; temp != null; temp = temp.next) { if (temp.value.Equals(obj)) return true; } } return false; } public void Delete(Object obj) { if (!IsEmpty()) { if (head == tail && head.value.Equals(obj)) head = tail = null; else if (head.value.Equals(obj)) head = head.next; else { // temp_prev为删除值的前驱节点 for (LinkListNode temp_prev = head, temp = head.next; temp != null; temp_prev = temp_prev.next, temp = temp.next) { if (temp.value.Equals(obj)) { temp_prev.next = temp.next; // 设置前驱节点的next为下个节点 if (temp == tail) tail = temp_prev; temp = null; break; } } } } } public Object GetHead() { return this.head.value; } public Object GetTail() { return this.tail.value; } public void Clear() { do { Delete(head.value); } while (!IsEmpty()); } }
双向链表:
public interface ILinkList { bool IsEmpty(); void Unshift(Object obj); Object Shift(); void Push(Object obj); Object Pop(); bool Contain(Object obj); void Delete(Object obj); void PrintAll(); Object GetHead(); Object GetTail(); void Clear(); } class LinkedNode { public Object value; public LinkedNode prev; public LinkedNode next; public LinkedNode(Object value, LinkedNode prev, LinkedNode next) { this.value = value; this.next = next; this.prev = prev; } public LinkedNode(Object value) { this.value = value; } } // 双向链表 public class DoubleLinkelist : ILinkList { private LinkedNode head, tail; public DoubleLinkelist() { head = tail = null; } public bool IsEmpty() { return head == null; } public void Unshift(Object obj) { if (IsEmpty()) head = tail = new LinkedNode(obj); else { head = new LinkedNode(obj, null, head); head.next.prev = head; } } public Object Shift() { if (IsEmpty()) throw new NullReferenceException(); Object obj = head.value; if (head == tail) head = tail = null; else { head = head.next; head.prev = null; } return obj; } public void Push(Object obj) { if (IsEmpty()) head = tail = new LinkedNode(obj); else { tail = new LinkedNode(obj, tail, null); tail.prev.next = tail; } } public Object Pop() { if (IsEmpty()) throw new NullReferenceException(); Object value = tail.value; if (head == tail) head = tail = null; else { tail = tail.prev; tail.next = null; } return value; } public bool Contain(Object obj) { if (IsEmpty()) return false; else { for (LinkedNode temp = head; temp != null; temp = temp.next) if (temp.value.Equals(obj)) return true; } return false; } public void Delete(Object obj) { if (IsEmpty()) throw new NullReferenceException(); if (head == tail) head = tail = null; else { for (LinkedNode temp = head; temp != null; temp = temp.next) { if (temp.value.Equals(obj)) { if (temp.value.Equals(obj)) { if (temp == tail) { tail = tail.prev; tail.next = null; break; } else if (temp == head) { head.next.prev = null; head = head.next; } else temp.prev.next = temp.next; } } } } } public void PrintAll() { string result = ""; for (LinkedNode temp = head; temp != null; temp = temp.next) result += " " + temp.value.ToString(); Console.WriteLine(result); } public Object GetHead() { return this.head.value; } public Object GetTail() { return this.tail.value; } public void Clear() { do { Delete(head.value); } while (!IsEmpty()); } }
栈是限定仅在表尾进行插入或删除操作的一种特殊的线性表。因此,对栈来说,表尾端有特殊含义,称为栈顶(top), 表头端成为栈底(bottom),栈的修改是按后进先出的原则进行的。 不含元素的空表成为空栈。
// 链表实现 public class MyLinkedStack<T> { public LinkedNode<T> bottom; // 栈底 public LinkedNode<T> top; // 栈顶 private int count = 0; public int Count => count; public MyLinkedStack() { bottom = new LinkedNode<T>(); top = bottom; } // 推入栈元素 public void Push(T value) { LinkedNode<T> node = new LinkedNode<T>(value); node.Next = top; top = node; count++; } // 删除并抛出 public T Pop() { if (top == bottom) throw new Exception("Stack is empty"); // 空栈 T value = top.Value; top = top.Next; count--; return value; } // 输出栈元素 public T Pull() { if (top == bottom) throw new Exception("Stack is empty"); // 空栈 return top.Value; } } public class LinkedNode<T> { public T Value { get; set; } public LinkedNode<T> Next { get; set; } public LinkedNode() { } public LinkedNode(T value) => Value = value; }
// 数组实现 public class MyStack<T> { private T[] items; private int top; private int count; public int Count => count; public int Capacity => items == null ? 0 : items.Length; public MyStack(int capacity = 10) { items = new T[capacity + 1]; top = 0; count = 0; } // 推入栈元素 public void Push(T item) { if (count == Capacity) Expansion(); items[top] = item; count++; top++; } // 删除并抛出 public T Pop() { if (count == 0) throw new Exception("Stack is empty"); top--; T value = items[top]; count--; return value; } // 取出栈元素 public T Pull() { if (count == 0) throw new Exception("Stack is empty"); return items[top - 1]; } // 扩容 private void Expansion() { int newCapacity = Capacity * 2; T[] newItems = new T[newCapacity]; for (int i = 0; i < top; i++) newItems[i] = items[i]; items = newItems; } }
队列也是一种特殊的线性表,只在表头(也称为队头)进行删除操作,只在表尾(也称为队尾)进行插入操作。由于队列的修改是按先进先出的原则,所以队列又称为先进先出(First In First Out)表,简称FIFO表。
// 链表实现队列 public class LinkedQueue<T> { public LinkedNode<T> front; public LinkedNode<T> rear; private int count; public int Count => count; public LinkedQueue() { count = 0; } // 加入队列元素 public void Enqueue(T value) { LinkedNode<T> node = new LinkedNode<T>(value); if (front == null) { front = node; rear = front; } else { rear.Next = node; rear = node; } count++; } // 取出队列元素并删除 public T Dequeue() { if (front == null) throw new Exception("Queue is empty"); // 空队列 T value = front.Value; front = front.Next; count--; return value; } // 取出队列元素 public T Pull() { if (front == null) throw new Exception("Queue is empty"); // 空队列 return front.Value; } } // 链表节点 public class LinkedNode<T> { public T Value { get; set; } public LinkedNode<T> Next { get; set; } public LinkedNode() { } public LinkedNode(T value) => Value = value; }
// 数组实现队列 public class MyQueue<T> { private T[] items; public int front; public int rear; private int count; public int Count => count; public int Capacity => items == null ? 0 : items.Length; public MyQueue(int capacity = 10) { items = new T[capacity]; front = 0; rear = 0; count = 0; } // 加入队列元素 public void Enqueue(T item) { if (count >= Capacity) Expansion(); items[rear] = item; rear = (rear + 1) % Capacity; count++; } // 取出队列元素 public T Dequeue() { if (count == 0) throw new Exception("The queue is empty"); // 空队列 T value = items[front]; count--; front = (front + 1) % Capacity; return value; } // 查看队列元素 public T Pull() { if (count == 0) throw new Exception("Queue is empty"); // 空队列 return items[front]; } // 扩容 private void Expansion() { int newCapacity = Capacity * 2; T[] newItems = new T[newCapacity]; for (int i = front; i < count; i++) newItems[i - front] = items[i % Capacity]; items = newItems; front = 0; rear = count; } }
哈希表(Hash table,也叫散列表),是一种有限连续的地址空间,用以存储按散列函数计算得到相应散列地址的数据记录。通常散列表的存储空间是一个一维数组,散列地址是数组的下标。
using System; using UnityEngine; public class MyHashTable { // 初始化哈希表 int[] hashTable; // 创建哈希表 public void CreateHashTable(int[] intArray) { hashTable = new int[intArray.Length]; for (int i = 0; i < intArray.Length; i++) { Insert(intArray[i]); } } public void ShowData() { Debug.Log("展示哈希表中的数据:" + String.Join(",", hashTable)); } /// <summary> /// 插入 /// </summary> /// <param name="value">待插入值</param> public void Insert(int value) { // 哈希函数,除留余数法 int hashAddress = Hash(value); // 如果不为0,则说明发生冲突 while (hashTable[hashAddress] != 0) { // 利用开放定址的线性探测法解决冲突 hashAddress = (++hashAddress) % hashTable.Length; } // 将待插入值存入字典中 hashTable[hashAddress] = value; } /// <summary> /// 哈希表查找 /// </summary> /// <param name="value">待查找的值</param> /// <returns>对应的下标</returns> public int Search(int value) { int hashAddress = Hash(value); // 冲突发生 while (hashTable[hashAddress] != value) { // 利用开放定址的线性探测法解决冲突 hashAddress = (++hashAddress) % hashTable.Length; // 查找到了开放单元或者循环回到原点,表示查找失败 if (hashTable[hashAddress] == 0 || hashAddress == Hash(value)) { return -1; } } // 查找成功,返回值的下标 return hashAddress; } /// <summary> /// 哈希函数(除留余数法) /// </summary> /// <param name="value"></param> /// <returns>返回数据的位置</returns> private int Hash(int value) { return value % hashTable.Length; } }
非线性结构每个元素可能与零个或者多个数据元素有着联系。常见的非线性结构有:二维数组,多维数组,广义表,树(二叉树等),图。
树结构是一类重要的非线性数据结构。树是以分支关系定义的层级结构,作为一种树状的数据结构,其数据节点之间的关系也如大树一样,将有限个节点根据不同层次关系进行排列,从而形成数据与数据之间的父子关系。
// 以实现简单二叉树为例子 class BinaryTree<TKey, TValue> where TKey : IComparable { public TKey Key { get; set; } // 键 public TValue Value { get; set; } // 值 BinaryTree<TKey, TValue> Left { get; set; } // 左分支 BinaryTree<TKey, TValue> Right { get; set; } // 右分支 // 构造函数初始化 public BinaryTree(TKey key, TValue value) { this.Key = key; this.Value = value; } // 查找功能 public TValue Search(TKey key) { int ret = key.CompareTo(this.Key); if (ret == 0) { return Value; } else { var subTree = ret < 0 ? Left : Right; if (subTree == null) { throw new KeyNotFoundException(); } else { return subTree.Search(key); } } } // 插入功能 public void Insert(TKey key, TValue value) { int ret = key.CompareTo(this.Key); if (ret == 0) { this.Value = value; } else { var subTree = ret < 0 ? Left : Right; if (subTree == null) { subTree = new BinaryTree<TKey, TValue>(key, value); if (ret < 0) Left = subTree; else Right = subTree; } else { subTree.Insert(key, value); } } } // 二叉排序树性的遍历 public void Visit(Action<TKey, TValue> visitor) { if (Left != null) { Left.Visit(visitor); } visitor(Key, Value); if (Right != null) { Right.Visit(visitor); } } }
当一棵优先级树是近似满二叉树时,称其为堆和偏序树。极小化优先数相应的堆称为极小化堆;极大化优先级树相应的堆称为极大化堆。
// 小根堆的实现 public class BinaryHeap<T> { //默认容量为6 private const int DEFAULT_CAPACITY = 6; private int mCount; private T[] mItems; private Comparer<T> mComparer; public BinaryHeap() : this(DEFAULT_CAPACITY) { } public BinaryHeap(int capacity) { if (capacity < 0) { throw new IndexOutOfRangeException(); } mItems = new T[capacity]; mComparer = Comparer<T>.Default; } // 增加元素到堆,并从后往前依次对各结点为根的子树进行筛选,使之成为堆,直到根结点 public bool Enqueue(T value) { if (mCount == mItems.Length) { ResizeItemStore(mItems.Length * 2); } mItems[mCount++] = value; int position = BubbleUp(mCount - 1); return (position == 0); } // 取出堆的最小值 public T Dequeue() { return Dequeue(true); } private T Dequeue(bool shrink) { if (mCount == 0) { throw new InvalidOperationException(); } T result = mItems[0]; if (mCount == 1) { mCount = 0; mItems[0] = default(T); } else { --mCount; //取序列最后的元素放在堆顶 mItems[0] = mItems[mCount]; mItems[mCount] = default(T); // 维护堆的结构 BubbleDown(); } if (shrink) { ShrinkStore(); } return result; } private void ShrinkStore() { // 如果容量不足一半以上,默认容量会下降。 if (mItems.Length > DEFAULT_CAPACITY && mCount < (mItems.Length >> 1)) { int newSize = Math.Max( DEFAULT_CAPACITY, (((mCount / DEFAULT_CAPACITY) + 1) * DEFAULT_CAPACITY)); ResizeItemStore(newSize); } } private void ResizeItemStore(int newSize) { if (mCount < newSize || DEFAULT_CAPACITY <= newSize) { return; } T[] temp = new T[newSize]; Array.Copy(mItems, 0, temp, 0, mCount); mItems = temp; } public void Clear() { mCount = 0; mItems = new T[DEFAULT_CAPACITY]; } // 从前往后依次对各结点为根的子树进行筛选,使之成为堆,直到序列最后的节点 private void BubbleDown() { int parent = 0; int leftChild = (parent * 2) + 1; while (leftChild < mCount) { // 找到子节点中较小的那个 int rightChild = leftChild + 1; int bestChild = (rightChild < mCount && mComparer.Compare(mItems[rightChild], mItems[leftChild]) < 0) ? rightChild : leftChild; if (mComparer.Compare(mItems[bestChild], mItems[parent]) < 0) { // 如果子节点小于父节点, 交换子节点和父节点 T temp = mItems[parent]; mItems[parent] = mItems[bestChild]; mItems[bestChild] = temp; parent = bestChild; leftChild = (parent * 2) + 1; } else { break; } } } // 从后往前依次对各结点为根的子树进行筛选,使之成为堆,直到根结点 private int BubbleUp(int startIndex) { while (startIndex > 0) { int parent = (startIndex - 1) / 2; //如果子节点小于父节点,交换子节点和父节点 if (mComparer.Compare(mItems[startIndex], mItems[parent]) < 0) { T temp = mItems[startIndex]; mItems[startIndex] = mItems[parent]; mItems[parent] = temp; } else { break; } startIndex = parent; } return startIndex; } }
图(Graph)G由V和E两个集合组成,记为G=(V, E)。其中V是顶点的有穷非空集合,E是V中顶点偶对的有穷集合,这些顶点偶对称为边。通常,也将图G的顶点集合和边集合分别为V(G)和E(G)。E(G)可以是空集,此时图G中只有顶点而没有边。
public class Vertex { public string Data; public bool IsVisited; public Vertex(string Vertexdata) { Data = Vertexdata; } } // 图的简单实现 public class Graph { //图中所能包含的点上限 private const int Number = 10; //顶点数组 private Vertex[] vertiexes; //邻接矩阵 public int[,] adjmatrix; //统计当前图中有几个点 int numVerts = 0; //初始化图 public Graph() { //初始化邻接矩阵和顶点数组 adjmatrix = new Int32[Number, Number]; vertiexes = new Vertex[Number]; //将代表邻接矩阵的表全初始化为0 for (int i = 0; i < Number; i++) { for (int j = 0; j < Number; j++) { adjmatrix[i, j] = 0; } } } //向图中添加节点 public void AddVertex(String v) { vertiexes[numVerts] = new Vertex(v); numVerts++; } //向图中添加有向边 public void AddEdge(int vertex1, int vertex2) { adjmatrix[vertex1, vertex2] = 1; //adjmatrix[vertex2, vertex1] = 1; } //显示点 public void DisplayVert(int vertexPosition) { Console.WriteLine(vertiexes[vertexPosition] + " "); } }
本章部分图文内容来源于网站和书籍资料提供, 自己整理收集,方便学习参考,版权属于原作者。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。