\documentclass[a4paper,11pt,oneside,openany]{article} \usepackage{CJK} \usepackage{times} \usepackage{listings} \usepackage{xcolor} \usepackage{color} \usepackage[top=1in, bottom=1in, left=0.75in, right=0.65in]{geometry} %边距 \usepackage{indentfirst} \setlength{\parskip}{0.7ex plus0.3ex minus0.3ex} \begin{document} \lstset{numbers=left, numberstyle=\tiny, keywordstyle=\color{blue!70}, commentstyle=\color{red!50!green!50!blue!50}, frame=shadowbox, rulesepcolor=\color{red!20!green!20!blue!20},xleftmargin=0pt,xrightmargin=6pt, breakindent=0pt} \begin{CJK*}{GBK}{com} \title{数据结构作业-2.19} \maketitle \date{} Made by CTeX. Source code on http://blog.csdn.net/c0de4fun. All rights reserved. 数据结构2.19题 【题目要求】:已知线性表中的元素以值递增有序排列,并以单链表作为存储结构。要求写一个{\color{red}{\CJKfamily{hei} 高效}}的算法删除链表中值大于mink并且小于maxk的元素 且释放被删除结点的空间,并分析算法的时间复杂度。注意,mink和maxk未必在链表中。 【算法分析】:题目中强调了高效,我们在这里采取的是“空间换时间”的策略,亦根据链表中元素构造一个整个区间的二叉查找树。树上所有非 叶子结点代表的是元素的{\color{red}{\CJKfamily{hei}区间}},而叶子结点则代表对应的元素,如果我们令 \begin{equation} N = LengthOf(mink,maxk) \end{equation} 那么很显然,在这棵树上搜索{\color{red}{\CJKfamily{hei}单个元素}}的时间复杂度为 \begin{equation} TimeComplexityOfSearchSingleElement=\Theta(\lg N) \end{equation} 因为对于区间中的每一个元素,我们都要做一次搜索(无论该元素是否存在!) \noindent 所以,删除该链表中所有位于(mink,maxk)的元素的总体的时间复杂度为 \begin{equation} TimeComplexity = O(N\lg N) \end{equation} 其空间复杂度为 \begin{equation} SpaceComplexity = O(N\lg N) \end{equation} 【算法改进及比较】: 一、改进时间复杂度 为了改进时间复杂度,我们可以开一个hash表,将hash表的key设置为链表中元素的value,hash表的value设置为链表中元素的位置。这样我们可以在 \begin{math} O(1) \end{math} 的时间复杂度删除对应元素,并且对元素的存取也可以达到 \begin{math} O(1) \end{math} 的级别。我们选择二叉搜索树的原因,是因为觉得这道题应该考的是二分搜索:-) 二、与二分搜索的比较 事实上,这道题如果用二分搜索做,可以节省一半的空间开销,但是二分搜索在这道题中有一个问题,亦即{\color{red}{\CJKfamily{hei}区间(mink,maxk)中的元素未必全部都在链表中}} 因此需要做一些额外的处理来保证二分搜索能找到对应的区间中的元素,而这无疑增加了程序的复杂程度。 【程序源代码】: \clearpage \begin{lstlisting}[language={[ANSI]C}] #include <iostream> #include <algorithm> #include <cstdlib> #include <cstring> #include <cstdio> #include <map> using namespace std; const int EMPTY_VALUE = 0; const int MAX_SIZE = 10000; int nums[MAX_SIZE]; int next[MAX_SIZE]; int tree[MAX_SIZE*2]; map<int,int> hash; int comp(const void *a,const void *b) { return *(int*)a - *(int*)b; } void buildTree(const int l ,const int r) { int mid = (l+r)/2; if( l == r) { tree[mid] = hash[nums[mid]]; return; } buildTree(l,mid); buildTree(mid+1,r); } void del(const int l, const int r, const int dest) { int mid = (l+r)/2; if( nums[tree[mid]] == dest ) { cout << "Found " << nums[tree[mid]] << " in linkTable!" << endl; int k = nums[tree[mid]]; hash[nums[tree[mid]]] = 0; //First node is dummy. nums[tree[mid]] = EMPTY_VALUE; cout << "Del " << k << " successfully!" << endl; return; } if( r <= mid || l >= mid ) { return; } else { if( nums[tree[mid]] > dest ) del(l,mid,dest); if( nums[tree[mid]] < dest ) del(mid+1,r,dest); } } int main() { int num; hash.clear(); memset(nums,EMPTY_VALUE,sizeof(nums)); cout << "Please enter the Number of elements you want to sort " <<endl; cin >> num; for(int i = 1 ; i <= num ; i++) { cout << "Please enter the " << i << "th element" << endl; cin >> nums[i]; hash[nums[i]] = i; } qsort(nums,num+1,sizeof(nums[0]),comp); buildTree(1,num); cout << "Please enter the boundary of [L,R] " << endl; int left,right; cout << "The left is: "; cin >> left; cout << "The right is: "; cin >> right; for(int i = left+1 ; i < right ; i++) { del(1,num,i); } cout << "The link table after deleting..." << endl; for(int i = 0 ; i <= num ; i++) { int cnt = 1; if( nums[i] != EMPTY_VALUE ) { cout << nums[i] << " "; cnt++; } if ((cnt % 5) == 0) cout << endl; } return 0; } \end{lstlisting} \end{CJK*} \end{document}