数据结构和算法学习笔记(1)

抓紧时间学习了,一步一个脚印,绝不松懈,这是本系列的第一篇,算法和数据结构是程序员你的必修课,面试看懂别人复杂的程序这些都是基本功,需要加强,所以有了下面的文章,2010.2.4 6.24分

数据结构和算法学习笔记(1)

首先给出一个最基本的结论:我们做算法和结构其实说白了,就是空间换时间,或者时间换空间。要看需求了。一般的情况下,大家都喜欢空间换时间。因为内存大嘛,总是用啊用不完。

最基本的一些结构就略过了,从栈开始吧。

1 堆栈

堆栈即为栈,下面是一个数组实现的栈:该栈固定大小,即存放的元素最大值固定,数组第0位表示栈底。如果需要存入不确定数量的数据,请使用链表来实现栈。

class Stack

         {

         public:

                   Stack(int iAmount = 10);   //设置栈所在数组的最大值

                   ~Stack();

                   int Pop(int&iVal);       //出栈

                   int Push(intiVal);       //入栈

                   int Top(int&iVal);     //栈顶元素

         private:

                   int *m_pData;       //栈所在数据首地址。。简单起见用int应该改用泛型

                   int m_iCount;            //使用的元素个数

                   int m_iAmount;        //栈最大元素个数

         };

Stack::Stack(int iAmount)

         {

         m_pData= new int[iAmount];//连续的数组,其中固定大小来划分栈中元素

         m_iCount= 0;                 //初始化栈内元素为0个

         m_iAmount= iAmount;

         }

Stack::~Stack()

         {

         delete m_pData;

         }

int Stack::Pop(int&iVal)

         {

         if(m_iCount>0)

                   {

                   --m_iCount;                  //栈内元素--

                   iVal= m_pData[m_iCount];    

                   return 1;

                   }

         return 0;

         }

int Stack::Push(int iVal)

         {

         if(m_iCount<m_iAmount)

                   {

                   m_pData[m_iCount]= iVal;

                   ++m_iCount;

                   return 1;

                   }

         return 0;

         }

int Stack::Top(int&iVal)

         {

         if(m_iCount>0 && m_iCount<=m_iAmount)

                   {

                   iVal= m_pData[m_iCount-1];

                   return 1;

                   }

         return 0;

         }

 

2 队列

下面是一个利用队列来对树进行广度优先检索。

广度优先区别于深度优先,即优先遍历最靠近根节点的各个节点:

我们的算法是:
1,根节点入队
2,出队一个节点,算一次遍历,直到队列为空
3,将刚出队的节点的子节点入队
4,转到2

队列的状况如下图:

 

 

// The Node

//////////////////////////////////////////////////////////////////////////

struct Node  //树的每个节点

         {

         Node(char cChar, intiSubNodeNum=0);

         ~Node();

         char m_cChar;               //该节点编号例如(A.B.C……)

         int m_iSubNodeNum;  //该节点的子节点数目

         Node**m_arrNodePointer; //指向子节点

         };

 

Node::Node(char cChar, intiSubNodeNum)

         {

         m_cChar= cChar;

         m_iSubNodeNum= iSubNodeNum;

         if(iSubNodeNum!=0)

                   m_arrNodePointer= new Node*[iSubNodeNum];

         else

                   m_arrNodePointer= NULL;

         }

 

Node::~Node()

         {

         if(m_arrNodePointer!=NULL)

                   delete[] m_arrNodePointer;

         }

 

// The Queue

//////////////////////////////////////////////////////////////////////////

class Queue

         {

         public:

                   Queue(int iAmount=10);

                   ~Queue();

                   //return 0 means failed, return 1 means succeeded.

                   int Enqueue(Node* node);

                   int Dequeue(Node* & node);

         private:

                   int m_iAmount;

                   int m_iCount;

                   Node**m_ppFixed; //The pointer array to implement thequeue.

                   int m_iHead;

                   int m_iTail;

         };

 

Queue::Queue(int iAmount)

         {

         m_iCount= 0; //队列中已经使用的元素

         m_iAmount= iAmount; //队列最大元素个数

         m_ppFixed= new Node*[iAmount]; //初始化一个数组保存元素

         m_iHead= 0;   //头位置,即取元素位置,在数组开头

         m_iTail= iAmount-1; //插入元素位置。在数组尾部

         }

 

Queue::~Queue()

         {

         delete[] m_ppFixed;

         }

 

int Queue::Enqueue(Node* node)

         {

         if(m_iCount<m_iAmount)

                   {

                   ++m_iTail;

                   if(m_iTail > m_iAmount-1)

                            m_iTail= 0;

                   m_ppFixed[m_iTail]= node;

                   ++m_iCount;

                   return 1;

                   }

         else

                   return 0;

         }

 

intQueue::Dequeue(Node* & node)

         {

         if(m_iCount>0)

                   {

                   node= m_ppFixed[m_iHead];

                   ++m_iHead;

                   if(m_iHead > m_iAmount-1)

                            m_iHead= 0;

                   --m_iCount;

                   return 1;

                   }

         else

                   return 0;

         }

 

// Main

//////////////////////////////////////////////////////////////////////////

int main(int argc, char* argv[])

         {

         //Construct the tree.

         NodenA('A', 3);

         Node nB('B',2);

         Node nC('C');

         Node nD('D',3);

         Node nE('E');

         Node nF('F',2);

         Node nG('G');

         Node nH('H',1);

         Node nI('I');

         Node nJ('J');

         Node nK('K');

         Node nL('L');

         nA.m_arrNodePointer[0] = &nB; //指向子节点

         nA.m_arrNodePointer[1] = &nC;

         nA.m_arrNodePointer[2] = &nD;

         nB.m_arrNodePointer[0] = &nE;

         nB.m_arrNodePointer[1] = &nF;

         nD.m_arrNodePointer[0] = &nG;

         nD.m_arrNodePointer[1] = &nH;

         nD.m_arrNodePointer[2] = &nI;

         nF.m_arrNodePointer[0] = &nJ;

         nF.m_arrNodePointer[1]= &nK;

         nH.m_arrNodePointer[0]= &nL;

         Queueque;

         que.Enqueue(&nA);  //根节点A入队列

         Node*pNode;

         while (que.Dequeue(pNode)==1)  //从队列中取出一个元素(第一次自然是A);第二次取出B

                   {

                   printf("%c ", pNode->m_cChar);   //第一次输出A;第二次输出B

                   int i;

                   for(i=0; i<pNode->m_iSubNodeNum; i++)  //第一次为A的三个子节点B,C,D入队列;第二次将B的子节点E,F加入队列。

                            {

                            que.Enqueue(pNode->m_arrNodePointer[i]);

                            }

                   }

         return 0;

         }

 

3 二分查找

//参数:有序数组、要查找的数字、最低位(一般为)、最高位(一般为数组最大下标)

static int

dichotomy_search(short a[], ems_int32 num, ems_int32 low, ems_int32high)

         {

         ems_int32mid;

         if(low > high)

                   {

                   return FATAL_ERROR; //没有查到。。

                   }

         mid= (low + high)/2;     //中间

         if(num == a[mid])

                   {

                   return mid;   //找到

                   }

         else

                   {

                   if(num < a[mid])  //在左边

                            {

                            return dichotomy_search(a, num, low, mid-1);

                            }

                   else  //在右边

                            {

                            return dichotomy_search(a, num, mid+1, high);

                            }

                   }

         }

 

4 散列

一般就是一个数组,通过散列算法定位到数组某个元素,当然这样可能会重复,你可以继续散列,或者做链表等等方法来处理。

1,除法散列法
最直观的一种,上图使用的就是这种散列法,公式:
index = value % 16
学过汇编的都知道,求模数其实是通过一个除法运算得到的,所以叫“除法散列法”。

2,平方散列法
求index是非常频繁的操作,而乘法的运算要比除法来得省时(对现在的CPU来说,估计我们感觉不出来),所以我们考虑把除法换成乘法和一个位移操作。公式:
index = (value * value) >> 28
如果数值分配比较均匀的话这种方法能得到不错的结果,但我上面画的那个图的各个元素的值算出来的index都是0——非常失败。也许你还有个问题,value如果很大,value * value不会溢出吗?答案是会的,但我们这个乘法不关心溢出,因为我们根本不是为了获取相乘结果,而是为了获取index。

可能有其他类型的输入,随机应变吧。

 

5 树

深度优先遍历又可分为:前序遍历(Preorder Traversal),后序遍历(Postorder Traversal)和中序遍历(Inorder Traversal),其中中序遍历只有对二叉树才有意义

//////////////////////////////////////////////////////////////////////////

struct TreeNode //树节点

         {

         char m_cVal;  //节点值

         TreeNode*m_pLeft; //左右子节点

         TreeNode*m_pRight;

         TreeNode(char cVal);

         };

 

TreeNode::TreeNode(char cVal)

         {

         m_cVal= cVal;

         m_pLeft= 0;

         m_pRight= 0;

         }

         //      A

         //    /   \

         //  /     \

         // B       C

         //   \     / \

         //   D   E   F

         //     \       \

         //     G       H

         //             / \

         //            I  J

         //           / \

         //          K  L

// TreeNode

中序遍历:从A开始

TreeNode p = 根节点A;

         while( p != NULL)

                   {

                   if(p有左节点)

                            {

                            P入栈 //1

                            p= p的左子节点//2

                            }

                   else if (p有右子节点)

                            {

                            输出P //1

                            p= p的右子节点//2

                            }

                   else //P无子节点了

                            {

                            输出P

                            如果栈里有值,弹出一个

                            如果栈里没有值了     break;

                            }

                   }

另外可以用递归实现,很简单。

 

如果需要查找和插入都很快,那么无疑应该选用二叉查找树。但是这种树的删除效率略低。如果用在只需要查找和插入的地方,比如构建属性表,空间信息数据则非常好。

 

但是,应该了解,二叉搜索树如果根节点的值选的不好或者插入的顺序不好,会使树非常之深,导致搜索插入效率急剧降低。那么就需要平衡二叉搜索树了。平衡二叉树的查找和删除和二叉搜索树一模一样。关键是构造的时候算法问题,主要分为4种情况,具体的算法可网上参阅。

 

6 二叉堆

一种特殊的队列,总是最小的元素先出。插入和取出都很快速,复杂度logn

基本概念:

 1 二叉堆是一种特殊的完全二叉树,完全二叉树的最大特点在于不需要指针来表明左右节点。可以直接利用数组来保存完全二叉树,利用偏移来找到需要的元素。

2 其中最小的元素总在根节点。

3 入队原则:将新加入的元素放到树(数组)的最后面,然后依次和父节点比较,如果比父节点小,则交换位置。如此循环,知道无法交换为止。

4 出队原则:不好写。略

 

//交换个整形

//example:SWAP_TWO_INT(7,8)

// a = 0111^1000 =0000

// b = 1000^0000 =1000

// a = 0000^1000 =0111

#define SWAP_TWO_INT(a, b) \

         a^=b;b^=a; a^=b;

 

class CBinaryHeap

         {

         public:

                   CBinaryHeap(int iSize = 100);

                   ~CBinaryHeap();

                   int Enqueue(intiVal);

                   int Dequeue(int&iVal);

                   int GetMin(int&iVal);

#ifdef _DEBUG

                   void PrintQueue();

#endif

         protected:

                   int *m_pData; //保存二叉堆的数组

                   int m_iSize;    //二叉堆最大容量

                   int m_iAmount; //目前数目

         };

 

CBinaryHeap::CBinaryHeap(int iSize)

         {

         m_pData= new int[iSize];

         m_iSize= iSize;

         m_iAmount= 0;

         }

 

CBinaryHeap::~CBinaryHeap()

         {

         delete[] m_pData;

         }

 

#ifdef _DEBUG

int CBinaryHeap::Enqueue(intiVal) //入队列

         {

         if(m_iAmount==m_iSize)

                   return 0;

         //Put this value to the end of the array.

         m_pData[m_iAmount]= iVal; //值放到数组最后,即二叉堆的最后

         ++m_iAmount;

         int iIndex = m_iAmount - 1;

         while(m_pData[iIndex] < m_pData[(iIndex-1)/2]) //循环和上一层比较,如果小于上一层则交换位置。

                   {

                   //Swap the two value

                   SWAP_TWO_INT(m_pData[iIndex],m_pData[(iIndex-1)/2])

                            iIndex= (iIndex-1)/2;//完全二叉树在数组中的位置固定

                   }

         return 1;

         }

#endif

 

int CBinaryHeap::Dequeue(int&iVal)//出队列

         {

         if(m_iAmount==0)

                   return 0;

         iVal= m_pData[0]; //返回根节点

         int iIndex = 0;

         while (iIndex*2 < m_iAmount)

                   {

                   int iLeft = (iIndex*2+1 <m_iAmount)?(iIndex*2+1):0;

                   int iRight = (iIndex*2+2 <m_iAmount)?(iIndex*2+2):0;

                   if(iLeft && iRight) //Both left and right exists. 将根节点的子节点中较小的元素和根节点交换。

                            {

                            if(m_pData[iLeft]<m_pData[iRight])

                                     {

                                     SWAP_TWO_INT(m_pData[iIndex],m_pData[iLeft])

                                               iIndex= iLeft;

                                     }

                            else

                                     {

                                     SWAP_TWO_INT(m_pData[iIndex],m_pData[iRight])

                                               iIndex= iRight;

                                     }

                            }

                   else if(iLeft) //The iRight must be 0

                            {

                            SWAP_TWO_INT(m_pData[iIndex],m_pData[iLeft])

                                     iIndex= iLeft;

                            break;

                            }

                   else

                            {

                            break;

                            }

                   }

         //Move the last element to the blank position.

         //Of course, if it is the blank one, forget it.

         if(iIndex!=m_iAmount-1)

                   {

                   m_pData[iIndex]= m_pData[m_iAmount-1]; //将最后一个元素移到目前根节点所在的位置

                   //Try to move this element to the top as high as possible.

                   while(m_pData[iIndex] < m_pData[(iIndex-1)/2])

                            {

                            //Swap the two value

                            SWAP_TWO_INT(m_pData[iIndex],m_pData[(iIndex-1)/2])

                                     iIndex= (iIndex-1)/2;

                            }

                   }

         --m_iAmount;

         return 1;

         }

 

int CBinaryHeap::GetMin(int&iVal)

         {

         if(m_iAmount==0)

                   return 0;

         iVal= m_pData[0];

         return 1;

         }

 

void CBinaryHeap::PrintQueue()

         {

         int i;

         for(i=0; i<m_iAmount; i++)

                   {

                   printf("%d ", m_pData[i]);

                   }

         printf("\n");

         }

 

int main(int argc, char* argv[])

         {

         CBinaryHeapbh;

         bh.Enqueue(4);  //入队

         bh.Enqueue(1);

         bh.Enqueue(3);

         bh.Enqueue(2);

         bh.Enqueue(6);

         bh.Enqueue(5);

#ifdef _DEBUG

         bh.PrintQueue();

#endif

         int iVal;

         bh.Dequeue(iVal);//出队

         bh.Dequeue(iVal);

#ifdef _DEBUG

         bh.PrintQueue();

#endif

         return 0;

       }

 

7 排序算法

7.1 //冒泡排序

void BubblerSort(int *pArray,int iElementNum)

         {

         int i, j, x;

         for(i=0; i<iElementNum-1; i++)  //

                   {

                   for(j=0; j<iElementNum-1-i; j++)

                            {

                            if(pArray[j]>pArray[j+1])

                                     {

                                     x= pArray[j];

   &nbs p;                                 pArray[j]= pArray[j+1];

                                     pArray[j+1]= x;

                                     }

                            }

                   }

         }

//内部每一次循环将数组中最大的元素移到最后

//外部循环n-1次排序完毕,

//复杂度n*n/2 = n*n

 

 

7.2 //直接插入排序

//第一次取数组前个排序,第二次将第三个元素插入前面已经排好序的个数里面

//第三次将第个元素插入到前面个已排好序的元素里面

void StraightInsertionSort(int*pArray, int iElementNum)

         {

         int i, j, k;

         for(i=0; i<iElementNum; i++)

                   {

                   int iHandling = pArray[i];

                   for(j=i; j>0; j--)   //循环比较查找要插入的位置。。

                            {

                            if(iHandling>=pArray[j-1]) //找到要插入的位置

                                     break; 

                            }

                   for(k=i; k>j; k--) //将要插入的元素插入到指定位置,后面的元素依次顺移

                            pArray[k]= pArray[k-1];

                   pArray[j]= iHandling;

                   }

         }

 

7.3 //二分插入排序

//和直接插入排序基本一样,只是在插入元素的时候利用了二分查找

void BinaryInsertionSort(int*pArray, int iElementNum)

         {

         int i, j, k;

         for(i=0; i<iElementNum; i++)

                   {

                   int iHandling = pArray[i];

                   int iLeft = 0;

                   int iRight = i-1;

                   while(iLeft<=iRight) //二分查找要插入的位置

                            {

                            int iMiddle = (iLeft+iRight)/2;

                            if(iHandling < pArray[iMiddle])

                                     {

                                     iRight= iMiddle-1;

                                     }

                            else if(iHandling> pArray[iMiddle])

                                     {

                                     iLeft= iMiddle+1;

                                     }

                            else

                                     {

                                     j= iMiddle + 1;

                                     break;

                                     }

                            }

                   if(iLeft>iRight)

                            j= iLeft; //如果没有找到,即不需要移动位置了。

                   for(k=i; k>j; k--)

                            pArray[k]= pArray[k-1];

                   pArray[j]= iHandling;

                   }

       }

 

//直接选择排序

//每循环一次把最大的元素取出来和最后一个元素交换

void StraightSelectionSort(int*pArray, int iElementNum)

         {

         int iEndIndex, i, iMaxIndex, x;

         for(iEndIndex=iElementNum-1; iEndIndex>0;iEndIndex--)

                   {

                   for(i=0, iMaxIndex=0; i<iEndIndex; i++) //找出最大的元素

                            {

                            if(pArray[i]>=pArray[iMaxIndex])

                                     iMaxIndex= i;

                            }

                   x= pArray[iMaxIndex];  //和最后一个元素交互

                   pArray[iMaxIndex]= pArray[iEndIndex];

                   pArray[iEndIndex]= x;

                   }   

       }

 

 

//快速排序,利用递归。

void QuickSort(int *pArray, int iElementNum)

         {

         int iTmp;

 

         //Select the pivot make it to the right side.

         int& iLeftIdx = pArray[0];

         int& iRightIdx = pArray[iElementNum-1];

         int& iMiddleIdx = pArray[(iElementNum-1)/2];

         if(iLeftIdx>iMiddleIdx)

                   {

                   iTmp= iLeftIdx;

                   iLeftIdx= iMiddleIdx;

                   iMiddleIdx= iTmp;

                   }

         if(iRightIdx>iMiddleIdx)

                   {

                   iTmp= iRightIdx;

                   iRightIdx= iMiddleIdx;

                   iMiddleIdx= iTmp;

                   }

         if(iLeftIdx>iRightIdx)

                   {

                   iTmp= iLeftIdx;

                   iLeftIdx= iRightIdx;

                   iRightIdx= iTmp;

                   }   //1:将左中右个元素的处于中间大小的元素放到数组的最后面,设为iPivot,为最开始的基础比较数据。

//2: 从数组第一个开始往后找到第一个大于iPivot的值,设为iLeft;从数组倒数第二个开始往前找到第一个小于iPivot的值iRight。然后交换iLeft和iRight。

         //Make pivot's left element and right element.

         int iLeft = 0;

         int iRight = iElementNum-2;

         int& iPivot = pArray[iElementNum-1];

         while (1)

                   {

                   while (iLeft<iRight &&pArray[iLeft]<iPivot) ++iLeft;

                   while (iLeft<iRight &&pArray[iRight]>=iPivot) --iRight;

                   if(iLeft>=iRight)

                            break;

                   iTmp= pArray[iLeft];

                   pArray[iLeft]= pArray[iRight];

                   pArray[iRight]= iTmp;  //交换iLeft和iRight

                   }

 

         //Make the i

         if(pArray[iLeft]>iPivot)  //这次交换有2个目的 1 小的在前面 2使比较的元素更接近平均值

                   {

                   iTmp= pArray[iLeft];

                   pArray[iLeft]= iPivot;

                   iPivot= iTmp;

                   }

 

         if(iLeft>1)

                   QuickSort(pArray,iLeft); //对前半部分排序

 

         if(iElementNum-iLeft-1>=1)

                   QuickSort(&pArray[iLeft+1],iElementNum-iLeft-1); //对后半部分排序

       }

 

//桶排序,例如buckets[100]=10 表示pArray中数值为100的元素有10个

void BucketSort(int *pArray, int iElementNum)
{
    int buckets[RAND_MAX]; // RAND_MAX 这个值需要囊括所有的pArray中的元素,不好把握。
    memset(buckets, 0, sizeof(buckets));
    int i;
    for(i=0; i<iElementNum; i++)
    {
        ++buckets[pArray[i]-1];
    }

    int iAdded = 0;
    for(i=0; i<RAND_MAX; i++)
    {
        while((buckets[i]--)>0)
        {
            pArray[iAdded++] = i;
        }
    }
}

posted @ 2010-02-04 18:49  熊健  阅读(701)  评论(0编辑  收藏  举报