4-2 数组栈、4-3链表栈、4-4数组栈、链表栈性能比较
栈的相关操作
Stack<E>
操作 | 描述 |
---|---|
void Push(e) | 添加元素 e 到栈顶 |
E Pop() | 删除并返回栈顶元素 |
E Peek() | 查询栈顶元素(不删除) |
int Count | 返回栈中总元素数目 |
bool IsEmpty | 检查栈是否为空(空返回 true ) |
栈的接口设计
IStack.cs
interface IStack<E>
{
int Count { get; }
bool IsEmpty { get; }
//添加元素
void Push(E e);
//删除元素
E Pop();
//查看元素
E Peek();
}
4-2 数组栈
利用动态数组,实现栈
数组栈的代码实现
Array1.cs
using System;
using System.Text;
namespace DataStucture
{
class Array1<E> //E 相当于一个占位符,可以是任意的名字
{
//动态数组的代码编写
//静态数组元素的存储
private E[] data;
//静态数组实际存了多少个元素
private int N;
//为动态数组实现一个初始化的构造函数
public Array1(int capacity) //capacity 容量
{
data = new E[capacity];
N = 0;
}
//无参的构造函数
//public Array1()
//{
// data = new int[10];
// N = 0;
//}
//或
public Array1() : this(10){ }
//用户可访问的三个公开的属性
//访问数组的容量
public int Capacity
{
get { return data.Length; }
}
//访问动态数组实际的元素有多少
public int Count
{
get { return N; }
}
//判断动态数组是否为空
public bool IsEmpty
{
get { return N == 0; }
}
//往数组中添加元素
public void Add(int index, E e)
{
if (index<0 || index>N)
{
throw new ArgumentNullException("数组索引越界");
}
if (N==data.Length)
{
ResetCapacity(2 * data.Length);
}
for (int i = N-1; i >= index; i--)
{
data[i + 1] = data[i];
}
data[index] = e;
N++;
}
//复用Add方法,为用户提供更加简便的添加方法
//在列表的尾部添加元素
public void AddLast(E e)
{
Add(N, e);
}
//在列表的头部添加元素
public void AddFirst(E e)
{
Add(0, e);
}
//方便索引查找
public E Get(int index)
{
if (index<0 || index >N)
{
throw new AggregateException("数组索引越界");
}
return data[index];
}
//索引头部
public E GetFirst()
{
return Get(0);
}
//索引尾部部
public E GetLast()
{
return Get(N-1);
}
//修改
public void Set(int index, E newE)
{
if (index < 0 || index > N)
{
throw new AggregateException("数组索引越界");
}
data[index] = newE;
}
//判断想要删除的 元素 是否在列表里
public bool Contains(int e)
{
for (int i = 0; i < N; i++)
{
if (data[i].Equals(e))
{
return true;
}
}
return false;
}
//数组存在在哪个位置
public int Indexof(int e)
{
for (int i = 0; i < N; i++)
{
//找到这个元素,返回的这个元素的索引
if (data[i].Equals(e))
{
return i;
}
}
return -1;
}
//删除 数组中某个位置的元素
public E RemoveAt(int index)
{
if(index<0 || index>=N)
{
throw new ArgumentException("数组索引越界");
}
//保存要删除的元素
E del = data[index];
//删除逻辑
for (int i = index+1; i <= N-1; i++)
{
data[i - 1] = data[i];
}
N--;
//对原来位置的中的元素,进行一个回收
data[N] = default(E);
if(N==data.Length/4)
{
ResetCapacity(data.Length / 2);
}
return del;
}
public E RemoveFirst()
{
return RemoveAt(0);
}
public E RemoveLast()
{
return RemoveAt(N - 1);
}
//明确的想要删除某个元素
public void Remove(int e)
{
int index = Indexof(e);
if (index!=-1)
{
RemoveAt(index);
}
}
//扩容数组
private void ResetCapacity(int newCapacity)
{
E[] newData = new E[newCapacity];
for (int i = 0; i < N; i++)
{
newData[i] = data[i];
}
data = newData;
}
//重写一个Tostring的方法
public override string ToString()
{
StringBuilder res = new StringBuilder();
//string.Format():格式化将指定字符串中的格式项替换为两个指定对象的字符串表示形式。
//res.Append("[");用于向 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();
}
}
}
Array1Stack.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace DataStucture
{
class Array1Stack<E>:IStack<E> //实现接口
{
private Array1<E> arr;
public int Count { get { return arr.Count; } }
public bool IsEmpty { get { return arr.IsEmpty; } }
//有参数的动态数组
public Array1Stack(int capacity)
{
arr = new Array1<E>(capacity);
}
//无参数的动态数组
public Array1Stack()
{
arr = new Array1<E>();
}
public void Push(E e)
{
//在数组的尾部添加元素
arr.AddLast(e);
}
public E Pop()
{
//在数组的尾部删除元素
return arr.RemoveLast();
}
public E Peek()
{
//查看数组尾部的元素
return arr.GetLast();
}
public override string ToString()
{
return "Stack:" + arr.ToString() + "top";
}
}
}
Program.cs
using System;
namespace DataStucture
{
class Program
{
static void Main(string[] args)
{
Array1Stack<int> stack = new Array1Stack<int>();
for (int i = 0; i < 5; i++)
{
stack.Push(i);
Console.WriteLine(stack);
}
stack.Pop();
Console.WriteLine(stack);
Console.Read();
}
}
}
输出结果:
4-3 链表栈
利用链表(链表就是动态的),实现栈
链表栈的代码实现
LinkedList1.cs
using System;
using System.Text;
namespace DataStucture
{
/// <summary>
/// 链表的简单基础框架的编写
/// </summary>
/// <typeparam name="E"></typeparam>
class LinkedList1<E>
{
private class Node
{
public E e; //结点存储的元素
public Node next; //下一个结点的引用(指针)
//使用构造函数 进行类的一个初始化
//构造函数1:创建节点时知道下一个节点是谁
public Node(E e, Node next)
{
this.e = e;
this.next = next;
}
//用户不知道下一个节点是谁
//再 写一个构造函数 当创建最后一个节点时(如尾部添加)
public Node (E e)
{
this.e = e;
this.next = null; //表示这个节点是没有下一个节点的
}
//重写一个ToString方法 打印输出节点类的信息
public override string ToString()
{
return e.ToString();
}
}
private Node head; //记录链表的头部
private int N; //记实时记录元素数量
//为链表类实现一个构造函数:初始化状态 空链表
public LinkedList1()
{
head = null;
N = 0;
}
/// <summary>
/// 用户访问的属性
/// </summary>
//访问链表中有多少个元素
public int Count
{
get { return N; }
}
//访问链表是否为空
public bool IsEmpty
{
get { return N == 0; }
}
public void Add(int index,E e)
{
if (index<0 || index>N)
{
throw new ArgumentException("非法索引");
}
if(index ==0)
{
//Node node = new Node(e);
//node.next = node;
//head = node;
head = new Node(e, head);
}
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;
pre.next = new Node(e, pre.next);
}
N++;
}
//用户使用
public void AddFirst(E e)
{
Add(0, e);
}
public void AddLast(E e)
{
Add(N, e);
}
//查询链表中的某个结点
public E Get(int index)
{
if(index<0 || index>N)
{
throw new ArgumentException("索引非法");
}
Node cur = head;
for (int i = 0; i < index; i++)
{
cur = cur.next;
}
return cur.e;
}
//查询链表中的头结点
public E GetFirst()
{
return Get(0);
}
//查询链表中的尾结点
public E GetLast()
{
return Get(N - 1);
}
//修改 将指定位置的结点进行修改
public void Set(int index,E newE)
{
if (index < 0 || index > N)
{
throw new ArgumentException("索引非法");
}
Node cur = head;
for (int i = 0; i < index; i++)
{
cur = cur.next;
}
cur.e = newE;
}
//查看链表中是否包含某个元素
public bool Contains(E e)
{
Node cur = head;
while (cur!=null)
{
if(cur.e.Equals(e))
{
return true;
}
cur = cur.next;
}
return false;
}
//删除链表中的结点
public E RemoveAt(int index)
{
if(index<0 || index>N)
{
throw new ArgumentException("索引非法");
}
if(index==0)
{
Node delNode = head;
head=head.next;
N--;
return delNode.e;
}
else
{
Node pre = head;
for (int i = 0; i < index-1; i++)
{
pre = pre.next;
}
Node delNode = pre.next;
pre.next = delNode.next;
N--;
return delNode.e;
}
}
//用户实现方法
public E RemoveFirst()
{
return RemoveAt(0);
}
public E RemoveLast()
{
return RemoveAt(N - 1);
}
//删除 链表中指定的结点
public void Remove(E e)
{
if(head==null)
{
return;
}
if(head.e.Equals(e))
{
head = head.next;
N--;
}
else
{
Node cur = head;
Node pre = null;
while (cur!=null)
{
if(cur.e.Equals(e))
{
break;
}
pre = cur;
cur = cur.next;
}
if(cur!=null)
{
pre.next = pre.next.next;
N--;
}
}
}
//输出重写
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();
}
}
}
LinkedList1Stack.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace DataStucture
{
class LinkedList1Stack<E>:IStack<E>
{
private LinkedList1<E> list;
//构造函数
public LinkedList1Stack()
{
list = new LinkedList1<E>(); //初始化
}
public int Count { get { return list.Count; } }
public bool IsEmpty { get { return list.IsEmpty; } }
public E Peek()
{
//在链表的头部添加
return list.GetFirst();
}
public E Pop()
{
return list.RemoveFirst();
}
public void Push(E e)
{
list.AddFirst(e);
}
public override string ToString()
{
return "Stack:top" + list.ToString();
}
}
}
Program.cs
using System;
namespace DataStucture
{
class Program
{
static void Main(string[] args)
{
LinkedList1Stack<int> stack = new LinkedList1Stack<int>();
for (int i = 0; i < 5; i++)
{
stack.Push(i);
Console.WriteLine(stack);
}
stack.Pop();
Console.WriteLine(stack);
Console.Read();
}
}
}
输出结果:
栈的性能测试
时间复杂度
栈 | Push(e) | Pop() | Peek() | Count | IsEmpty |
---|---|---|---|---|---|
数组栈 | O(1)* | O(1) | O(1) | O(1) | O(1) |
链表栈 | O(1) | O(1) | O(1) | O(1) | O(1) |
数组栈和链表栈的性能测试方法: 时间复杂度皆为O(N)
Program.cs
using System;
using System.Collections.Generic;
using System.Diagnostics;
namespace DataStucture
{
class Program
{
//测试栈性能 返回的时间类型是一个long长整型
public static long TestStack(IStack<int> stack, int N)
{
//计时器
Stopwatch t = new Stopwatch();
t.Start();
for (int i = 0; i < N; i++)
{
stack.Push(i);//进栈
}
for (int i = 0; i < N; i++)
{
stack.Pop(); //出栈
}
t.Stop();
return t.ElapsedMilliseconds;
}
static void Main(string[] args)
{
int N = 10000000;
//数组栈
Array1Stack<int> array1Stack = new Array1Stack<int>();
//扩容操作
//Array1Stack<int> array1Stack = new Array1Stack<int>(N); 更快
long t1 = TestStack(array1Stack, N);
Console.WriteLine("Array1Stack'time:'" + t1 + "ms");
//数组栈
LinkedList1Stack<int> linkedList1Stack = new LinkedList1Stack<int>();
long t2 = TestStack(linkedList1Stack, N);
Console.WriteLine("linkedList1Stack'time:'" + t2 + "ms");
//C#提供的栈
Stack<int> stack = new Stack<int>();
Stopwatch t = new Stopwatch();
t.Start();
for (int i = 0; i < N; i++)
{
stack.Push(i);//进栈
}
for (int i = 0; i < N; i++)
{
stack.Pop(); //出栈
}
t.Stop();
Console.WriteLine("Stack'time:'" + t.ElapsedMilliseconds + "ms");
Console.Read();
}
}
}
输出结果:
数组栈
未扩容:
扩容:
链表栈
C#提供的栈
Stopwatch.ElapsedMilliseconds
Stopwatch 是C#中一个用于测量时间间隔的类,它可以用来计算代码执行所需的时间或性能数据。
使用 Elapsed 属性或 ElapsedMilliseconds 属性获取运行时间。Elapsed 属性返回一个 TimeSpan 实例,而 ElapsedMilliseconds 返回毫秒数。
为什么链表栈的时间长?
通过new操作在内存中找到一个可以使用的位置 ,开空间 ,在内存中的位置是不连续的;
需要存储元素和下一个节点的引用