五、双端队列Double-ended Queues

1 队列、栈和堆的基本知识

1.1栈内存(先进后出)

  栈内存首先是一片内存区域,存储的都是局部变量,凡是定义在函数中的都是局部变量,函数外的是全局变量,for循环内部定义的也是局部变量,是先加载函数才能进行局部变量的定义,所以方法先进栈,然后再定义变量,变量有自己的作用域,一旦离开作用域,变量就会被释放。栈内存的更新速度很快,因为局部变量的生命周期都很短。
  限定仅在表尾进行插入和删除操作的线性表。这一端被称为栈顶,相对地,把另一端称为栈底。向一个栈删除元素又称作出栈或退栈,它是把栈顶元素删除掉,使其相邻的元素成为新的栈顶元素。
请添加图片描述

1.2 堆内存

  存储的是数组和对象(其实数组就是对象),凡是new建立的都是在堆中,堆中存放的都是实体(对象),实体用于封装数据,而且是封装多个(实体的多个属性),如果一个数据消失,这个实体也没有消失,还可以用,所以堆是不会随时释放的,但是栈不一样,栈里存放的都是单个变量,变量被释放了,那就没有了。堆里的实体虽然不会被释放,但是会被当成垃圾,Java有垃圾回收机制不定时的收取。

1.3 栈和堆的联系

  主函数先进栈,在栈中定义一个变量arr,接下来为arr赋值,但是右边不是一个具体值,是一个实体。实体创建在堆里,在堆里首先通过new关键字开辟一个空间,内存在存储数据的时候都是通过地址来体现的,地址是一块连续的二进制,然后给这个实体分配一个内存地址。数组都是有一个索引,数组这个实体在堆内存中产生之后每一个空间都会进行默认的初始化(这是堆内存的特点,未初始化的数据是不能用的,但在堆里是可以用的,因为初始化过了,但是在栈里没有),不同的类型初始化的值不一样。所以堆和栈里就创建了变量和实体:

int
main(int argc, char *argv[]){
	int *arr = (int *)malloc(3 * sizeof(int));
}

请添加图片描述

1.4 队列(先进先出)

  具体查看参考3:队列及特点和应用详解

2 双端队列

  是一种具有队列和栈的性质的数据结构,双端队列中的元素可以从两端弹出,其限定插入和删除操作在表的两端进行。

2.1 结构体定义

typedef struct _GQueue GQueue;
struct _GQueue
{
  GList *head; /* 指向队列的第一个元素 */
  GList *tail; /* 指向队列的最后一个元素 */
  guint  length; /* 队列元素个数 */
};

2.2 函数总结

2.2.1 队列创建

(1)栈区创建和释放

GQueue*  g_queue_new            (void);
void     g_queue_free           (GQueue           *queue);

(2)堆区创建和释放

void     g_queue_init           (GQueue           *queue);
void     g_queue_clear          (GQueue           *queue);

2.2.2 队列压入数据

(1)队列头部压入

void     g_queue_push_head      (GQueue           *queue,
                                 gpointer          data);

(2)队列尾部压入

void     g_queue_push_tail      (GQueue           *queue,
                                 gpointer          data);

(3)队列根据位置number压入

void     g_queue_push_nth       (GQueue           *queue,
                                 gpointer          data,
                                 gint              n);

(4) 队列根据节点地址在前面压入

void     g_queue_insert_before  (GQueue           *queue,
                                 GList            *sibling,
                                 gpointer          data);

(5)队列根据节点地址在后面压入

GLIB_AVAILABLE_IN_ALL
void     g_queue_insert_after   (GQueue           *queue,
                                 GList            *sibling,
                                 gpointer          data);

(6)队列根据用户自定义排序函数在前面压入

void     g_queue_insert_sorted  (GQueue           *queue,
                                 gpointer          data,
                                 GCompareDataFunc  func,
                                 gpointer          user_data);

2.2.3 取出data同时删除在队列中删除

(1)取出头节点data

gpointer g_queue_pop_head       (GQueue           *queue);

(2)取出尾节点data

gpointer g_queue_pop_tail       (GQueue           *queue);

(2)根据位置取出头节点data

gpointer g_queue_pop_nth        (GQueue           *queue,
                                 guint             n);

2.2.4 只查看data不删除

(1)头节点

gpointer g_queue_peek_head      (GQueue           *queue);

(2)尾节点

gpointer g_queue_peek_tail      (GQueue           *queue);

(3)根据位置查询节点

gpointer g_queue_peek_nth       (GQueue           *queue,
                                 guint             n);

2.2.5只查询Glist不删除

(1)根据data查询

GList *  g_queue_find           (GQueue           *queue,
                                 gconstpointer     data);

(2)根据用户定义func查询

GList *  g_queue_find_custom    (GQueue           *queue,
                                 gconstpointer     data,
                                 GCompareFunc      func);

2.2.6根据data删除队列

(1)只删除第一个相同data

gboolean g_queue_remove         (GQueue           *queue,
                                 gconstpointer     data);

(2)删除所有相同data

guint    g_queue_remove_all     (GQueue           *queue,
                                 gconstpointer     data);

2.2.7 反转队列

void     g_queue_reverse        (GQueue           *queue);

2.2.8 判断队列是否为空

gboolean g_queue_is_empty       (GQueue           *queue);

3 代码

#include<stdio.h>
#include<glib.h>

typedef struct _map map;
struct _map
{
  gint key;
  gchar *value;
};

map m[10] = {{0, "zero"},
             {1, "one"},
             {2, "two"},
             {3, "three"},
             {4, "four"},
             {5, "five"},
             {6, "six"},
             {7, "seven"},
             {8, "eight"},
             {9, "nine"}};

static void
myPrintInit(gpointer p1, gpointer fmt){
  g_print(fmt, *(gint *)p1);
}

static void
myPrintStr(gpointer p1, gpointer fmt){
  g_print(fmt, (gchar *)p1);
}

static void
test_queue_1(void){
  /*创建队列*/
  GQueue *queue = g_queue_new();
  /*判断队列是否为空,空返回1*/
  gboolean b = g_queue_is_empty(queue);
  g_print("The queue should be empty now.\t\tResult: %s.\n", b ? "YES" : "NO");

  gint i;
  for(i = 0; i < sizeof(m) / sizeof(m[0]); i++)
    /* 将数据data压入队列尾部 */
    g_queue_push_tail(queue, m[i].value);
  
  g_print("Now the queue[after push tail]:\n");
  /* 遍历队列 */
  g_queue_foreach(queue, myPrintStr, "%s, ");
  g_print("\n");

  /*获得队列元素*/
  g_print("The length should be '%d' now.\t\tResult: %d.\n",
          10, g_queue_get_length(queue));
  /*只查看元素,不取出*/
  g_print("head :%s\n",(gchar *)g_queue_peek_head(queue));
  g_print("tail :%s\n",(gchar *)queue->tail->data);

  g_print("3: %s\n",(gchar *)g_queue_peek_nth(queue, 3));

  /*取出两头元素,取出后,两头该元素就被删除了*/
  g_print("head :%s\n",(gchar *)g_queue_pop_head(queue));
  g_print("tail :%s\n",(gchar *)g_queue_pop_tail(queue));
  g_queue_foreach(queue, myPrintStr, "%s, ");
  g_print("\n");

  g_print("n : %s\n",(gchar *)g_queue_pop_nth(queue, 2));
  g_queue_foreach(queue, myPrintStr, "%s, ");
  g_print("\n");

  /*将数据压入队列头*/
  g_queue_push_head(queue,  m[0].value);
  /*指定位置压入*/
  g_queue_push_nth(queue, m[3].value, 3);
  g_queue_push_tail(queue,  m[9].value);
  g_queue_foreach(queue, myPrintStr, "%s, ");
  g_print("\n");

  /*释放队列*/
  g_queue_free(queue);
}

static gint
sort(gconstpointer p1, gconstpointer p2, gpointer user_data){/*正序*/
  gint32 a = *(gint*)(p1);
  gint32 b = *(gint*)(p2);
  return (a > b ? +1 : a == b ? 0 : -1);
}

static gint
sort_r(gconstpointer p1, gconstpointer p2, gpointer user_data){/*正序*/
  gint32 a = *(gint*)(p1);
  gint32 b = *(gint*)(p2);
  return (a < b ? +1 : a == b ? 0 : -1);
}

static gint
myCompareInt(gconstpointer p1, gconstpointer p2){
  const gint *a = p1;
  const gint *b = p2;
  return *a - *b;
}

static void
test_queue_2(void){
  GQueue *queue = NULL;
  queue = g_queue_new();

  gint i;
  for(i = 0; i< sizeof(m)/sizeof(m[0]); i++)
    g_queue_insert_sorted(queue, &m[i].key, sort_r, NULL);
  for(i = 0; i < queue->length; i++)
    g_print("%d, ", *(gint *)g_queue_peek_nth(queue, i));
  g_print("\n");

  /*根据data删除数据*/
  g_queue_remove(queue, &m[3].key);
  for(i = 0; i < queue->length; i++)
    g_print("%d, ", *(gint *)g_queue_peek_nth(queue, i));
  g_print("\n");

  g_queue_insert_after(queue, g_queue_find_custom(queue, &m[4].key, myCompareInt), &m[3].key);
  for(i = 0; i < queue->length; i++)
    g_print("%d, ", *(gint *)g_queue_peek_nth(queue, i));
  g_print("\n");

  g_queue_free(queue);
}

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

  test_queue_2();

  return 0;
}

参考1:数据结构(栈的基本概念和性质)
参考2:堆和栈的概念和联系
参考3:队列及特点和应用详解
参考4:队列和栈的基本性质和应用
参考5:Double-ended Queues Manual
参考6:例程参考博客

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值