我正在尝试为我的类中的每个方法和构造函数确定复杂度指标(Big-theta表示法),这些方法和构造函数使用数组和链接列表作为底层结构创建队列。具体来说,我不确定如何查看并快速确定每个方法或构造函数是O(k)还是O(n)。有人可以解释一下吗?
数组类:
public class UnboundedQueueArray<T> implements UnboundedQueue<T> {
// elements stored in array
private T[] elements;
// the number of elements currently in the queue
private int numElems;
// initial length of queue
private static final int INITIAL_LENGTH = 10;
/**
* Constructs the UnboundedQueueArray object.
*/
@SuppressWarnings("unchecked")
public UnboundedQueueArray() {
elements = (T[]) new Object[INITIAL_LENGTH];
numElems = 0;
}
/**
* This method inserts the specified element, unless the
* queue is full.
*
* @param o The element to be inserted.
*/
public void insert(T o) {
// checks and enlarges capacity if necessary
ensureExtraCapacity(1);
elements[numElems] = o;
numElems++;
}
// this helper method ensures there is always extra capacity
// in the queue to insert elements
@SuppressWarnings("unchecked")
private void ensureExtraCapacity(int extraCapacity) {
if(numElems + extraCapacity > elements.length) {
// double old capacity
int newCapacity = elements.length * 2 + extraCapacity;
// allocate new array
T[] newElements = (T[]) new Object[newCapacity];
// copy contents of old array into new array
for(int i = 0; i < length(); i++) {
newElements[i] = elements[i];
}
// replace old array with new array
elements = newElements;
}
}
/**
* This method returns the element at the front of the
* queue, unless the queue is empty.
*
* @return The element at the front of the queue.
* @throws EmptyQueueException If the queue is empty.
*/
public T front() throws EmptyQueueException {
if(length() == 0) {
throw new EmptyQueueException("Queue is empty.");
}
return elements[0];
}
/**
* This method retrieves and removes the element at the front
* of the queue, unless the queue is empty.
*
* @return The element at the front of the queue.
* @throws EmptyQueueException If the queue is empty.
*/
public T remove() throws EmptyQueueException {
if(length() == 0) {
throw new EmptyQueueException("Queue is empty.");
}
// retrieve element at front of queue
T frontElem = elements[0];
// shift elements to the left
for(int i = 1; i < length(); i++) {
elements[i - 1] = elements[i];
}
// "null out" last element
elements[numElems - 1] = null;
// decrement number of elements
numElems--;
return frontElem;
}
/**
* This method reports whether or not the queue contains
* element(s).
*
* @return If one or more elements exists or not.
*/
public boolean hasMember() {
return (length() > 0);
}
/**
* This method returns the current length of the queue.
*
* @return The length of the queue.
*/
public int length() {
return numElems;
}
/**
* This method provides a string representation of the queue.
*
* @return The String representation of the queue.
*/
public String toString() {
// for empty queue
if(length() == 0) {
String str = "[ " + length() + " : ]";
return str;
}
// fencepost algorithm
String str = "[ " + length() + " : " + elements[0];
for(int i = 1; i < length(); i++) {
str += ", " + elements[i];
}
str += " ]";
return str;
}
}
链接列表类:
public class UnboundedQueueLinkedList<T> implements UnboundedQueue<T> {
// the reference to the first link
private Link first;
// the reference to the last link (if it exists)
private Link last;
// the number of links in the queue
private int numLinks;
// initial length of queue
private static final int INITIAL_LENGTH = 10;
/**
* Constructs the UnboundedQueueLinkedList object.
*/
@SuppressWarnings("unchecked")
public UnboundedQueueLinkedList() {
first = null;
last = null;
numLinks = 0;
}
/**
* This method inserts the specified element, unless the
* queue is full.
*
* @param o The element to be inserted.
*/
public void insert(T o) {
Link newLink = new Link(o, null);
if(first == null) {
// we are adding the first link
first = newLink;
} else { // there are existing links, so add newLink after old last link
last.next = newLink;
}
// update the last link
last = newLink;
// increment the number of links in the queue
numLinks++;
}
/**
* This method returns the element at the front of the
* queue, unless the queue is empty.
*
* @return The element at the front of the queue.
* @throws EmptyQueueException If the queue is empty.
*/
@SuppressWarnings("unchecked")
public T front() throws EmptyQueueException {
if(length() == 0) {
throw new EmptyQueueException("Queue is empty.");
}
T frontElem;
// get link at front of queue
Link frontLink = getLinkAtPos(0);
frontElem = (T) frontLink.item;
return frontElem;
}
// this helper method gets the link at the specified position
private Link getLinkAtPos(int pos) {
Link p = first;
for(int i = 0; i < pos; i++) {
p = p.next;
}
return p;
}
/**
* This method retrieves and removes the element at the front
* of the queue, unless the queue is empty.
*
* @return The element at the front of the queue.
* @throws EmptyQueueException If the queue is empty.
*/
@SuppressWarnings("unchecked")
public T remove() throws EmptyQueueException {
if(length() == 0) {
throw new EmptyQueueException("Queue is empty.");
}
T removedElem;
removedElem = (T) first.item;
// remove "first" link
first = first.next;
// update "last" if necessary
if(first == null) {
last = null;
}
// decrement the number of links in the queue
numLinks--;
return removedElem;
}
/**
* This method reports whether or not the queue contains
* element(s).
*
* @return If one or more elements exists or not.
*/
public boolean hasMember() {
return (length() > 0);
}
/**
* This method returns the current length of the queue.
*
* @return The length of the queue.
*/
public int length() {
return numLinks;
}
/**
* This method provides a string representation of the queue.
*
* @return The String representation of the queue.
*/
public String toString() {
// for empty queue
if(length() == 0) {
String str = "[ " + length() + " : ]";
return str;
}
Link p = first;
String str = "[ " + length() + " : " + p.item;
for(int i = 1; i < numLinks; i++) {
p = p.next;
str += ", " + p.item;
}
str += " ]";
return str;
}
// this helper class creates the links that structure the list
class Link<T> {
// data associated with this link
public Object item;
// next link, or null if no next link
public Link next;
/**
* Constructs the Link object.
*
* @param item The data to be associated with this Link object.
* @param next The next link (or null if no next link exists).
*/
public Link(Object item, Link next) {
this.item = item;
this.next = next;
}
}
}
编辑:这是堆栈数组类:
public class UnboundedStackArray<T> implements UnboundedStack<T> {
// elements stored in array
private T[] elements;
// the number of elements currently in the stack
private int numElems;
// initial depth of stack
private static final int INITIAL_DEPTH = 10;
/**
* Constructs the UnboundedStackArray object.
*/
@SuppressWarnings("unchecked")
public UnboundedStackArray() {
elements = (T[]) new Object[INITIAL_DEPTH];
numElems = 0;
}
/**
* This method "pushes" an element onto the top of the stack.
*
* @param o The element to be "pushed" (or added) onto the top
* of the stack.
*/
public void push(T o) {
// ensure space to add element
ensureExtraCapacity(1);
elements[numElems] = o;
// increment the number of elements in the stack
numElems++;
}
// this helper method ensures there is always extra capacity
// in the stack to "push" (or add) elements onto top of stack
@SuppressWarnings("unchecked")
private void ensureExtraCapacity(int extraCapacity) {
if(numElems + extraCapacity > elements.length) {
// double old capacity
int newCapacity = elements.length * 2 + extraCapacity;
// allocate new array
T[] newElements = (T[]) new Object[newCapacity];
// copy contents of old array into new array
for(int i = 0; i < depth(); i++) {
newElements[i] = elements[i];
}
// replace old array with new array
elements = newElements;
}
}
/**
* This method retrieves the element at the top of the stack,
* unless the stack is empty.
*
* @return The element at the top of the stack.
* @throws EmptyStackException If the stack is empty.
*/
public T top() throws EmptyStackException {
if(depth() == 0) {
throw new EmptyStackException("Stack is empty");
}
return elements[numElems - 1];
}
/**
* This method retrieves and removes the element at the top of
* the stack, unless the stack is empty.
*
* @return The element at the top of the stack.
* @throws EmptyStackException If the stack is empty.
*/
public T pop() throws EmptyStackException {
if(depth() == 0) {
throw new EmptyStackException("Stack is empty");
}
// retrieve element at top of stack
T topElem = elements[numElems - 1];
// "null out" element at top of stack
elements[numElems - 1] = null;
// decrement number of elements
numElems--;
return topElem;
}
/**
* This method reports whether or not the stack contains
* element(s).
*
* @return If one or more elements exists or not.
*/
public boolean hasMember() {
return (depth() > 0);
}
/**
* This method returns the current depth (or length) of the stack.
*
* @return The depth of the stack.
*/
public int depth() {
return numElems;
}
/**
* This method provides a string representation of the stack.
*
* @return The String representation of the stack.
*/
public String toString() {
// for empty stack
if(depth() == 0) {
String str = "[ " + depth() + " : ]";
return str;
}
String str = "[ " + depth() + " : " + elements[numElems - 1];
for(int i = numElems - 2; i >= 0; i--) {
str += ", " + elements[i];
}
str += " ]";
return str;
}
}
这是堆栈链表类:
public class UnboundedStackLinkedList<T> implements UnboundedStack<T> {
// the reference to the first link
private Link first;
// the reference to the last link (if it exists)
private Link last;
// the number of links in the stack
private int numLinks;
// initial length of stack
private static final int INITIAL_LENGTH = 10;
/**
* Constructs the UnboundedStackLinkedList object.
*/
@SuppressWarnings("unchecked")
public UnboundedStackLinkedList() {
first = null;
last = null;
numLinks = 0;
}
/**
* This method "pushes" an element onto the top of the stack.
*
* @param o The element to be "pushed" (or added) onto the top
* of the stack.
*/
public void push(T o) {
Link newLink = new Link(o, null);
if(first == null) {
// add the first link
first = newLink;
} else { // there are existing links, so add newLink after old last link
last.next = newLink;
}
// update the last link
last = newLink;
// increment the number of links in the queue
numLinks++;
}
/**
* This method retrieves the element at the top of the stack,
* unless the stack is empty.
*
* @return The element at the top of the stack.
* @throws EmptyStackException If the stack is empty.
*/
@SuppressWarnings("unchecked")
public T top() throws EmptyStackException {
if(depth() == 0) {
throw new EmptyStackException("Stack is empty.");
}
T topElem;
// get link at front of queue
Link topLink = getLinkAtPos(numLinks - 1);
topElem = (T) topLink.item;
return topElem;
}
// this helper method gets the link at the specified position
private Link getLinkAtPos(int pos) {
Link p = first;
for(int i = 0; i < pos; i++) {
p = p.next;
}
return p;
}
/**
* This method retrieves and removes the element at the top of
* the stack, unless the stack is empty.
*
* @return The element at the top of the stack.
* @throws EmptyStackException If the stack is empty.
*/
@SuppressWarnings("unchecked")
public T pop() throws EmptyStackException {
if(depth() == 0) {
throw new EmptyStackException("Stack is empty.");
}
T removedElem;
removedElem = (T) last.item;
Link p = first;
for(int i = 0; i < depth() - 2; i++) {
p = p.next;
}
//p.next = null;
last = p;
// update "last" if necessary
if(first == null) {
last = null;
}
// decrement the number of links in the queue
numLinks--;
return removedElem;
}
/**
* This method reports whether or not the stack contains
* element(s).
*
* @return If one or more elements exists or not.
*/
public boolean hasMember() {
return (depth() > 0);
}
/**
* This method returns the current depth (or length) of the stack.
*
* @return The depth of the stack.
*/
public int depth() {
return numLinks;
}
/**
* This method provides a string representation of the stack.
*
* @return The String representation of the stack.
*/
@SuppressWarnings("unchecked")
public String toString() {
// for empty stack
if(depth() == 0) {
String str = "[ " + depth() + " : ]";
return str;
}
Link pL = last;
String str = "[ " + depth() + " : " + pL.item;
for(int i = numLinks - 2; i >= 0; i--) {
Link tempLink = getLinkAtPos(i);
str += ", " + tempLink.item;
}
str += " ]";
return str;
}
// this helper class creates the links that structure the list
class Link<T> {
/**
* Data associated with this link.
*/
public Object item;
/**
* Next link, or null if no next link.
*/
public Link next;
/**
* Constructs the Link object.
*
* @param item The data to be associated with this Link object.
* @param next The next link (or null if no next link exists).
*/
public Link(Object item, Link next) {
this.item = item;
this.next = next;
}
}
}
答案 0 :(得分:2)
数组类:
UnboundedQueueArray:O(1)
插入:O(1)
ensureExtraCapacity:O(n)(n =当前长度)
“O(n)是最坏情况,否则为O(1)”
前面:O(1)“总是O(n)”
长度:O(1)
Optimalization:
我强烈建议您使用堆栈来处理此行为,因为它会处理每个方法的复杂度为O(1)。
链接列表:
UnboundedQueueLinkedList:O(1)
插入:O(1)
行为似乎不正确
last.next = newLink;
last = newLink;
getLinkAtPos:O(pos)
删除:O(1)
长度:O(1)
可以做一些优化。
UnboundedQueueLinkedList.getLinkAtPos
如果你把它作为一个双链表,那么你可以从pos > length() / 2
开始。这将最坏情况复杂度降低到O(log(n))。
堆栈数组类:
UnboundedStackArray:O(1)
推:O(1)+ ensureExtraCapacity
ensureExtraCapacity:wcs是O(n),bcs是O(1)
顶部:O(1)
pop:O(1)
深度:O(1)
toString:O(n)
我绝对不使用数组作为堆栈,列表效率更高
堆栈链表:
UnboundedStackLinkedList:O(1)
推:O(1)
同样,看起来功能不正确
top:O(1)+ getLinkAtPos
getLinkAtPos:O(n)
pop:O(n)
深度:O(1)
toString:O($ n ^ 2 $)
这可能会更有效率。
如果您创建堆栈,则只需跟踪top
项。每次推送时,顶部项目都会被新项目替换,新项目的next
链接项目将是旧项目。
每次弹出时,弹出顶部项目并将其引用设置为新顶部。唯一超过O(1)
的方法是toString()
方法。
答案 1 :(得分:1)
@ user2581779:我将尝试解释O(n)的含义,但我不会检查你的代码。希望您在自己阅读完答案后能够回答这个问题(和其他相关问题)。
首先,你应该理解为什么使用O符号。例如,你可以在给定的计算机上使用几秒钟的执行时间,对吗?
嗯,这有很多问题:
好的,只是测量秒数不是一个好主意。但是你为什么不测量一些操作呢?
int a = 7 + 3
有多少次操作?a += b
有多少次?你必须考虑编译器优化以及底层汇编程序的外观)理论上,当您增加输入大小时,您总是对算法的开发方式更感兴趣。
因此,假设您有一个n
元素数组。您希望对这些元素进行排序。算法A需要n^2 + 1234
个操作,算法B需要n^1.9 + 123123123123123
个操作。哪一个更好? (请注意n
只是一个变量。我也可以将其命名为k
或whatever
)
很长一段时间,算法A的表现会更好。但是一旦B变得更好(有更多元素n),它将比A更好。
所以,你可以忘记添加常数术语。还有乘法常数。在实践中,乘法常数可能非常重要,但Big-O表示法会跳过这一点。
请注意,您关心最坏的情况。最好的案例同样容易检查,但不是很有趣。
平均案例分析难以正确完成。如果您想尝试,请搜索“accounting method”或“聚合方法”。
时空复杂性。对于大多数简单应用,只有时间复杂度很重要。但请注意,您经常可以进行时间/空间权衡。存储更多信息时,您的算法可能会更快。
Big-O表示法仅用于了解函数增长的强度。
正式:
让g : N -> R
成为一个函数。
O(g(n)) &:= {f(n) | \exists_{c > 0} \exists_{n_0 > 0} \forall_{n \geq n_0}: f(n) < c *g(n) }
(有关渲染版本,请参阅my site)
这意味着,如果说您的计划位于O(n)
是正确的,那么说它在O(n^2)
中也是正确的。 Big Theta符号使这更好,但更难以正确确定。
对于大多数程序来说,它非常简单:
n
。n
之上的一个循环意味着你处于O(n)
之中(尽管可能会更糟)O(n^2)
或更差log(n)
的内容,因为这是树的高度(但只有在你可以保证它是平衡的时候!)sum_{i=0}^n \sum_{j=i}^n 1 = sum_{i=0}^n i = (n^2 + n)/2 + 1
。需要一些练习来确定哪些操作是1
,哪些是n
或不同的操作,但只要您以数学方式编写它们,就可以使用Wolfram | Alpha来简化他们。有关示例,请参阅我的article about Gaussian elimination。答案 2 :(得分:0)
具体来说,我不确定如何查看并快速确定每个方法或构造函数是O(k)还是O(n)。
首先,浏览代码并找到仅包含O(1)代码的所有方法。这些方法不会有循环运行n次,也不会调用O(n)方法。由于它们直接运行,它们是O(1),即使它们在循环之外调用许多其他O(1)方法。如果该方法循环通过O(0)代码n次,则该方法为0(n)。如果它有多个不在循环内的O(n)操作,它仍然是O(n),尽管可能具有更大的n系数。如果它具有执行n次的O(n)代码,则为O(n ^ 2)。
<强>阵列强>
UnboundedQueueArray()是O(INITIAL_LENGTH),它是O(1),因为INITIAL_LENGTH是常量。 ensureExtraCapacity(int extraCapacity)是O(n),只要它扩展内部数组,因为它declares an array of size newCapacity(必须大于n)并运行一个循环n次,但是是O(1否则。
插入是让事情变得有趣的地方。大约每n次被调用一次,它会将ensureExtraCapacity称为O(n)操作。如果你每n次调用O(n)一次,你平均为O(n / n),或O(1)。但由于你偶尔会有O(n),我们给它一个特殊的名称:摊销的常数时间。
front,hasMember,长度为O(1),由于循环,remove是O(n),而toString只是O(n),因为我们可以在恒定时间内访问元素。
<强>链表强>
UnboundedQueueLinkedList (the constructor), insert, front, remove, hasMember,
和length
都是O(1)。 getLinkAtPos(int pos)
可以说是O(pos),但我们只称它为O(n),因为最坏的情况随着n变大而变大。
toString()
是O(n ^ 2),因为它执行O(n)操作,getLinkAtPos,n次。
备注:强>
如果你回到LinkedList的toString()的原始实现,你可以让它下到O(n),因为你避免在循环中调用getLinkAtPos。
public String toString() {
// for empty queue
if(length() == 0) {
String str = "[ " + length() + " : ]";
return str;
}
Link p = first;
String str = "[ " + length() + " : " + p.item;
for(int i = 1; i < numLinks; i++) {
p = p.next;
str += ", " + p.item;
}
str += " ]";
return str;
}